修改了数据集上传,修改了模型配置页面

This commit is contained in:
2026-01-14 16:35:22 +08:00
parent e1ab76a9a1
commit 43b6018a3e
19 changed files with 1058 additions and 1231 deletions

View File

@@ -34,85 +34,6 @@
}
}
</script>
<!-- 网格布局样式 -->
<style>
/* 2x2网格布局 - 通用 */
.grid-2x2 {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
grid-template-rows: repeat(2, auto) !important;
gap: 1rem !important;
}
/* 1x4网格布局 - 用于模型选择区域 */
.grid-1x4 {
display: grid !important;
grid-template-columns: repeat(4, 1fr) !important;
grid-template-rows: 1 !important;
gap: 1rem !important;
}
/* 结果展示区域的2x2布局 */
#comparisonResults .grid-2x2 {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
grid-template-rows: repeat(2, auto) !important;
gap: 1rem !important;
}
/* 模型选择区域的1x4布局 */
#compare-config-section .grid-1x4 {
display: grid !important;
grid-template-columns: repeat(4, 1fr) !important;
grid-template-rows: 1 !important;
gap: 1rem !important;
}
/* 确保结果展示的卡片是网格项 */
#comparisonResults .grid-2x2 > div {
width: 100%;
}
/* 确保模型输出卡片正确排列 */
#comparisonResults .grid-2x2 > div:nth-child(1) { grid-row: 1; grid-column: 1; } /* 模型A - 左上 */
#comparisonResults .grid-2x2 > div:nth-child(2) { grid-row: 1; grid-column: 2; } /* 模型B - 右上 */
#comparisonResults .grid-2x2 > div:nth-child(3) { grid-row: 2; grid-column: 1; } /* 模型C - 左下 */
#comparisonResults .grid-2x2 > div:nth-child(4) { grid-row: 2; grid-column: 2; } /* 模型D - 右下 */
/* 响应式调整 */
@media (max-width: 1200px) {
#compare-config-section .grid-1x4 {
grid-template-columns: repeat(2, 1fr) !important;
grid-template-rows: repeat(2, auto) !important;
gap: 0.75rem !important;
}
}
@media (max-width: 768px) {
#comparisonResults .grid-2x2 {
grid-template-columns: repeat(2, 1fr) !important;
grid-template-rows: repeat(2, auto) !important;
gap: 0.75rem !important;
}
}
@media (max-width: 640px) {
#compare-config-section .grid-1x4 {
grid-template-columns: repeat(1, 1fr) !important;
grid-template-rows: repeat(4, auto) !important;
gap: 0.75rem !important;
}
#comparisonResults .grid-2x2 {
grid-template-columns: repeat(1, 1fr) !important;
grid-template-rows: repeat(4, auto) !important;
gap: 0.75rem !important;
}
#comparisonResults .grid-2x2 > div:nth-child(1) { grid-row: 1; grid-column: 1; }
#comparisonResults .grid-2x2 > div:nth-child(2) { grid-row: 2; grid-column: 1; }
#comparisonResults .grid-2x2 > div:nth-child(3) { grid-row: 3; grid-column: 1; }
#comparisonResults .grid-2x2 > div:nth-child(4) { grid-row: 4; grid-column: 1; }
}
</style>
<style type="text/tailwindcss">
@layer utilities {
.gradient-blue {
@@ -515,7 +436,7 @@
</div>
<!-- 模型选择区 -->
<div class="grid-1x4 mb-4">
<div class="grid grid-cols-2 gap-4 mb-4">
<!-- 模型A -->
<div class="bg-gray-50 rounded-xl p-3 border-2 border-dashboard-primary/20">
<h3 class="text-lg font-semibold text-dashboard-text mb-3 flex items-center">
@@ -559,50 +480,6 @@
</div>
</div>
</div>
<!-- 模型C -->
<div class="bg-gray-50 rounded-xl p-3 border-2 border-gray-300 opacity-50 transition-opacity hover:opacity-100">
<h3 class="text-lg font-semibold text-dashboard-text mb-3 flex items-center">
<span class="w-6 h-6 rounded-full bg-gray-500 text-white flex items-center justify-center text-sm font-bold mr-2">C</span>
模型 C
</h3>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-dashboard-text mb-2">选择模型</label>
<select id="modelCSelect" class="w-full px-3 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-dashboard-primary/20">
<option value="">请选择模型C (可选)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-dashboard-text mb-2">模型信息</label>
<div id="modelCInfo" class="text-sm text-dashboard-textLight bg-white p-3 rounded-lg border border-gray-200">
请先选择模型
</div>
</div>
</div>
</div>
<!-- 模型D -->
<div class="bg-gray-50 rounded-xl p-3 border-2 border-gray-300 opacity-50 transition-opacity hover:opacity-100">
<h3 class="text-lg font-semibold text-dashboard-text mb-3 flex items-center">
<span class="w-6 h-6 rounded-full bg-gray-500 text-white flex items-center justify-center text-sm font-bold mr-2">D</span>
模型 D
</h3>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-dashboard-text mb-2">选择模型</label>
<select id="modelDSelect" class="w-full px-3 py-2 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-dashboard-primary/20">
<option value="">请选择模型D (可选)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-dashboard-text mb-2">模型信息</label>
<div id="modelDInfo" class="text-sm text-dashboard-textLight bg-white p-3 rounded-lg border border-gray-200">
请先选择模型
</div>
</div>
</div>
</div>
</div>
<!-- 测试输入区 -->
@@ -653,7 +530,7 @@
<span class="font-semibold">显示配置</span>
</button>
<div id="comparisonResults" class="hidden">
<div class="grid-2x2">
<div class="grid grid-cols-2 gap-4">
<!-- 模型A输出 -->
<div class="bg-white rounded-xl p-4 border border-gray-200">
<div class="flex items-center justify-between mb-4">
@@ -707,59 +584,18 @@
<div>Token数: <span id="modelBTokens">-</span></div>
</div>
</div>
</div>
<!-- 模型C输出 -->
<div class="bg-white rounded-xl p-4 border border-gray-200 hidden" id="modelCOutputCard">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-dashboard-text flex items-center">
<span class="w-6 h-6 rounded-full bg-gray-500 text-white flex items-center justify-center text-sm font-bold mr-2">C</span>
<span id="modelCName">模型C</span>
</h3>
<div class="flex space-x-2">
<button onclick="copyToClipboard('modelCOutput')" class="text-dashboard-textLight hover:text-dashboard-primary">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
</button>
</div>
</div>
<div class="mb-4">
<div class="text-xs text-dashboard-textLight mb-2">输出结果</div>
<div id="modelCOutput" class="bg-gray-50 p-4 rounded-lg border border-gray-200" style="min-height: 300px; max-height: 600px; overflow-y: auto;">
<div class="text-dashboard-textLight">等待对比结果...</div>
</div>
</div>
<div class="text-xs text-dashboard-textLight">
<div>响应时间: <span id="modelCTime">-</span></div>
<div>Token数: <span id="modelCTokens">-</span></div>
</div>
</div>
<!-- 模型D输出 -->
<div class="bg-white rounded-xl p-4 border border-gray-200 hidden" id="modelDOutputCard">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-dashboard-text flex items-center">
<span class="w-6 h-6 rounded-full bg-gray-500 text-white flex items-center justify-center text-sm font-bold mr-2">D</span>
<span id="modelDName">模型D</span>
</h3>
<div class="flex space-x-2">
<button onclick="copyToClipboard('modelDOutput')" class="text-dashboard-textLight hover:text-dashboard-primary">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
</button>
</div>
</div>
<div class="mb-4">
<div class="text-xs text-dashboard-textLight mb-2">输出结果</div>
<div id="modelDOutput" class="bg-gray-50 p-4 rounded-lg border border-gray-200" style="min-height: 300px; max-height: 600px; overflow-y: auto;">
<div class="text-dashboard-textLight">等待对比结果...</div>
</div>
</div>
<div class="text-xs text-dashboard-textLight">
<div>响应时间: <span id="modelDTime">-</span></div>
<div>Token数: <span id="modelDTokens">-</span></div>
</div>
<!-- 对比分析 -->
<div class="mt-4 bg-white rounded-xl p-4 border border-gray-200">
<h3 class="text-lg font-semibold text-dashboard-text mb-3 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
对比分析
</h3>
<div id="comparisonAnalysis" class="space-y-3">
<div class="text-dashboard-textLight">对比结果将在这里显示...</div>
</div>
</div>
</div>
@@ -2046,9 +1882,7 @@
let comparisonModels = {
modelA: null,
modelB: null,
modelC: null,
modelD: null
modelB: null
};
// 初始化模型对比页面
@@ -2087,16 +1921,12 @@
function loadComparisonModelList() {
const modelASelect = document.getElementById('modelASelect');
const modelBSelect = document.getElementById('modelBSelect');
const modelCSelect = document.getElementById('modelCSelect');
const modelDSelect = document.getElementById('modelDSelect');
if (!modelASelect || !modelBSelect) return;
// 清空现有选项
modelASelect.innerHTML = '<option value="">请选择模型A</option>';
modelBSelect.innerHTML = '<option value="">请选择模型B</option>';
if (modelCSelect) modelCSelect.innerHTML = '<option value="">请选择模型C (可选)</option>';
if (modelDSelect) modelDSelect.innerHTML = '<option value="">请选择模型D (可选)</option>';
// 添加模型选项
modelConfigs.forEach((model, index) => {
@@ -2109,20 +1939,6 @@
optionB.value = index;
optionB.textContent = `${model.name} (${getProviderName(model.provider)})`;
modelBSelect.appendChild(optionB);
if (modelCSelect) {
const optionC = document.createElement('option');
optionC.value = index;
optionC.textContent = `${model.name} (${getProviderName(model.provider)})`;
modelCSelect.appendChild(optionC);
}
if (modelDSelect) {
const optionD = document.createElement('option');
optionD.value = index;
optionD.textContent = `${model.name} (${getProviderName(model.provider)})`;
modelDSelect.appendChild(optionD);
}
});
}
@@ -2130,8 +1946,6 @@
function setupComparisonEventListeners() {
const modelASelect = document.getElementById('modelASelect');
const modelBSelect = document.getElementById('modelBSelect');
const modelCSelect = document.getElementById('modelCSelect');
const modelDSelect = document.getElementById('modelDSelect');
const testTemperature = document.getElementById('testTemperature');
if (modelASelect) {
@@ -2142,14 +1956,6 @@
modelBSelect.onchange = () => handleModelSelection('B', modelBSelect.value);
}
if (modelCSelect) {
modelCSelect.onchange = () => handleModelSelection('C', modelCSelect.value);
}
if (modelDSelect) {
modelDSelect.onchange = () => handleModelSelection('D', modelDSelect.value);
}
if (testTemperature) {
testTemperature.oninput = (e) => {
document.getElementById('tempDisplay').textContent = e.target.value;
@@ -2202,27 +2008,22 @@
function checkModelSelection() {
const modelASelect = document.getElementById('modelASelect');
const modelBSelect = document.getElementById('modelBSelect');
const modelCSelect = document.getElementById('modelCSelect');
const modelDSelect = document.getElementById('modelDSelect');
// 获取所有已选择的模型索引
const selected = [];
if (modelASelect && modelASelect.value) selected.push(modelASelect.value);
if (modelBSelect && modelBSelect.value) selected.push(modelBSelect.value);
if (modelCSelect && modelCSelect.value) selected.push(modelCSelect.value);
if (modelDSelect && modelDSelect.value) selected.push(modelDSelect.value);
if (modelASelect && modelBSelect) {
// 禁用已选择的模型
const selectedA = modelASelect.value;
const selectedB = modelBSelect.value;
// 为每个下拉框禁用已选择的模型
const selects = [modelASelect, modelBSelect, modelCSelect, modelDSelect];
selects.forEach(select => {
if (!select) return;
Array.from(select.options).forEach((option, index) => {
Array.from(modelASelect.options).forEach((option, index) => {
if (index === 0) return; // 跳过第一个选项
const optionValue = index.toString();
// 如果该选项在其他下拉框中被选中,则禁用
option.disabled = selected.includes(optionValue);
option.disabled = index.toString() === selectedB;
});
});
Array.from(modelBSelect.options).forEach((option, index) => {
if (index === 0) return; // 跳过第一个选项
option.disabled = index.toString() === selectedA;
});
}
}
// 折叠配置区
@@ -2322,22 +2123,15 @@
// 运行模型对比
async function runModelComparison() {
const modelA = comparisonModels.modelA;
const modelB = comparisonModels.modelB;
const prompt = document.getElementById('testPrompt').value.trim();
const temperature = parseFloat(document.getElementById('testTemperature').value);
const maxTokens = parseInt(document.getElementById('testMaxTokens').value);
// 获取所有选择的模型
const selectedModels = [];
for (let modelType of ['A', 'B', 'C', 'D']) {
const model = comparisonModels[`model${modelType}`];
if (model) {
selectedModels.push({ model, modelType });
}
}
// 验证输入
if (selectedModels.length < 2) {
alert('请至少选择两个模型进行对比');
if (!modelA || !modelB) {
alert('请选择两个模型进行对比');
return;
}
@@ -2353,37 +2147,22 @@
// 折叠配置区
collapseConfigSection();
// 更新模型名称
document.getElementById('modelAName').textContent = modelA.name;
document.getElementById('modelBName').textContent = modelB.name;
// 重置输出区域
resetOutputAreas();
// 显示已选择模型的流式输出状态,并显示/隐藏对应的卡片
selectedModels.forEach(({ model, modelType }) => {
// 更新模型名称
const nameElement = document.getElementById(`model${modelType}Name`);
if (nameElement) {
nameElement.textContent = model.name;
}
// 显示流式输出状态
const modelAOutput = document.getElementById('modelAOutput');
const modelBOutput = document.getElementById('modelBOutput');
// 显示对应的输出卡片
const outputCard = document.getElementById(`model${modelType}OutputCard`);
if (outputCard) {
outputCard.classList.remove('hidden');
}
// 显示流式输出状态
const outputElement = document.getElementById(`model${modelType}Output`);
if (outputElement) {
outputElement.innerHTML = '<div class="text-dashboard-text"><span class="animate-pulse">正在生成中...</span></div>';
}
});
// 隐藏未选择模型的输出卡片
for (let modelType of ['C', 'D']) {
const model = comparisonModels[`model${modelType}`];
const outputCard = document.getElementById(`model${modelType}OutputCard`);
if (outputCard && !model) {
outputCard.classList.add('hidden');
}
if (modelAOutput) {
modelAOutput.innerHTML = '<div class="text-dashboard-text"><span class="animate-pulse">正在生成中...</span></div>';
}
if (modelBOutput) {
modelBOutput.innerHTML = '<div class="text-dashboard-text"><span class="animate-pulse">正在生成中...</span></div>';
}
// 开始对比
@@ -2393,44 +2172,51 @@
try {
// 创建流式显示回调
const streamingCache = {};
const onChunkCallbacks = {};
const modelPromises = [];
const streamingCache = {
'A': { output: '', responseTime: 0, streaming: false },
'B': { output: '', responseTime: 0, streaming: false }
};
// 为每个选择的模型创建回调和Promise
selectedModels.forEach(({ model, modelType }) => {
streamingCache[modelType] = { output: '', responseTime: 0, streaming: false };
const onChunkA = (chunk) => {
streamingCache['A'].output = chunk.content;
streamingCache['A'].responseTime = Date.now();
streamingCache['A'].streaming = true;
displayStreamingModelResult('A', streamingCache['A']);
};
onChunkCallbacks[modelType] = (chunk) => {
streamingCache[modelType].output = chunk.content;
streamingCache[modelType].responseTime = Date.now();
streamingCache[modelType].streaming = true;
displayStreamingModelResult(modelType, streamingCache[modelType]);
};
const onChunkB = (chunk) => {
streamingCache['B'].output = chunk.content;
streamingCache['B'].responseTime = Date.now();
streamingCache['B'].streaming = true;
displayStreamingModelResult('B', streamingCache['B']);
};
// 创建模型调用Promise
const promise = withTimeout(
callRealModel(model, prompt, temperature, maxTokens, model.streaming ? onChunkCallbacks[modelType] : null),
60000
);
modelPromises.push({ promise, modelType });
});
// 并行运行两个模型(带超时控制)
const [resultA, resultB] = await Promise.allSettled([
withTimeout(callRealModel(modelA, prompt, temperature, maxTokens, modelA.streaming ? onChunkA : null), 60000), // 60秒超时
withTimeout(callRealModel(modelB, prompt, temperature, maxTokens, modelB.streaming ? onChunkB : null), 60000) // 60秒超时
]);
// 并行运行所有模型
const results = await Promise.allSettled(modelPromises.map(item => item.promise));
// 处理所有模型结果
results.forEach((result, index) => {
const modelType = modelPromises[index].modelType;
if (result.status === 'fulfilled') {
if (!result.value.streaming) {
displayModelResult(modelType, result.value);
}
} else {
displayModelError(modelType, result.reason);
// 处理模型A结果
if (resultA.status === 'fulfilled') {
if (!resultA.value.streaming) {
displayModelResult('A', resultA.value);
}
});
} else {
displayModelError('A', resultA.reason);
}
// 处理模型B结果
if (resultB.status === 'fulfilled') {
if (!resultB.value.streaming) {
displayModelResult('B', resultB.value);
}
} else {
displayModelError('B', resultB.reason);
}
// 生成对比分析
generateComparisonAnalysis(modelA, modelB);
} catch (error) {
console.error('对比过程中出错:', error);
@@ -2474,23 +2260,18 @@
// 检查是否启用流式输出
const contentType = response.headers.get('content-type');
const isStreaming = contentType?.includes('text/plain');
console.log('[流式调试] Content-Type:', contentType);
console.log('[流式调试] isStreaming:', isStreaming);
console.log('[流式调试] onChunk:', onChunk);
if (isStreaming) {
// 流式处理
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullContent = '';
let chunkCount = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
console.log('[流式调试] 收到数据块:', chunk);
const lines = chunk.split('\n');
for (const line of lines) {
@@ -2500,14 +2281,11 @@
try {
const parsed = JSON.parse(data);
console.log('[流式调试] 解析数据:', parsed);
if (parsed.content !== undefined) {
if (parsed.content) {
fullContent += parsed.content;
chunkCount++;
// 调用回调函数更新UI
if (onChunk) {
console.log('[流式调试] 调用回调函数, 当前内容长度:', fullContent.length);
onChunk({
content: fullContent,
delta: parsed.content,
@@ -2516,12 +2294,11 @@
}
}
if (parsed.done) {
console.log('[流式调试] 流式输出完成, 总块数:', chunkCount);
break;
}
}
} catch (e) {
console.error('[流式调试] 解析错误:', e);
// 忽略解析错误
}
}
}
@@ -2654,8 +2431,53 @@
tokensElement.textContent = '-';
}
});
const analysisElement = document.getElementById('comparisonAnalysis');
if (analysisElement) {
analysisElement.innerHTML = '<div class="text-dashboard-textLight">正在分析对比结果...</div>';
}
}
// 生成对比分析
function generateComparisonAnalysis(modelA, modelB) {
const analysisElement = document.getElementById('comparisonAnalysis');
if (!analysisElement) return;
const analysis = [
{
title: '响应速度',
content: '两个模型的响应时间对比分析'
},
{
title: '回答质量',
content: '基于内容完整性、准确性和有用性的评估'
},
{
title: '语言风格',
content: '分析两个模型的语言表达方式和风格特点'
},
{
title: '详细程度',
content: '比较回答的详细程度和信息密度'
},
{
title: '总体评价',
content: '综合两个模型在本次测试中的表现'
}
];
let analysisHTML = '';
analysis.forEach(item => {
analysisHTML += `
<div class="border-l-4 border-dashboard-primary pl-4">
<h4 class="font-semibold text-dashboard-text mb-1">${item.title}</h4>
<p class="text-sm text-dashboard-textLight">${item.content}</p>
</div>
`;
});
analysisElement.innerHTML = analysisHTML;
}
// 复制到剪贴板
function copyToClipboard(elementId) {