Files
YG_FT_Platform/web/pages/model-compare-chat.html
WIN-JHFT4D3SIVT\caoxiaozhu 40ca89fad5 1. 修改了应用端口的问题
2. 增加了创建虚拟环境的脚本和删除虚拟环境的脚本
2026-01-26 14:33:00 +08:00

469 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模型对比 - 远光软件微调平台</title>
<script src="../lib/tailwindcss/tailwind.js"></script>
<link href="../lib/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<style>
.form-input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
font-size: 0.875rem;
transition: border-color 0.2s, outline 0.2s;
}
.form-input:focus {
border-color: #1890ff;
outline: none;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
margin-bottom: 0.25rem;
}
.bg-primary { background-color: #1890ff; }
.text-primary { color: #1890ff; }
.border-primary { border-color: #1890ff; }
:root { --primary: #1890ff; }
/* 滑块样式 */
.slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
border-radius: 3px;
background: #e5e7eb;
outline: none;
}
.slider-thumb::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--primary);
cursor: pointer;
box-shadow: 0 2px 6px rgba(24, 144, 255, 0.3);
transition: transform 0.2s;
}
.slider-thumb::-webkit-slider-thumb:hover {
transform: scale(1.1);
}
/* 折叠动画 */
.input-section {
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
overflow: hidden;
}
.input-section.collapsed {
max-height: 0;
opacity: 0;
}
/* 流式输出光标 */
.typing-cursor::after {
content: '|';
animation: blink 1s infinite;
color: var(--primary);
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* 输出卡片动画 */
.output-card {
transition: all 0.3s ease;
}
.output-card.loading {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
/* 已选模型标签 */
.model-tag {
transition: all 0.2s;
}
.model-tag:hover {
border-color: #1890ff;
background-color: rgba(24, 144, 255, 0.05);
}
</style>
</head>
<body class="antialiased bg-gray-50">
<!-- 主内容区域 - 由 main.html 提供侧边栏和顶部导航 -->
<div class="bg-white rounded-lg shadow-sm">
<!-- 面包屑导航和返回按钮 -->
<div class="px-4 py-3 border-b border-gray-100 flex items-center justify-between">
<div class="flex items-center text-sm">
<a href="main.html?page=model-compare" class="text-primary hover:underline">模型对比</a>
<span class="mx-2 text-gray-300">/</span>
<span class="text-gray-800 font-medium" id="breadcrumbTask">对话</span>
</div>
<a href="main.html?page=model-compare" class="text-gray-500 hover:text-gray-700 text-sm flex items-center">
<i class="fa fa-arrow-left mr-1"></i>返回列表
</a>
</div>
<!-- 页面内容 -->
<div class="p-6">
<form id="chatForm">
<!-- 已配置模型展示 -->
<div class="mb-6">
<label class="form-label flex items-center mb-3">
<i class="fa fa-server text-primary mr-2"></i>
已配置模型
<span class="ml-2 text-xs text-gray-400 font-normal">(共 <span id="modelCount">0</span> 个)</span>
</label>
<div id="selectedModelsList" class="flex flex-wrap gap-2">
<!-- 模型标签将通过JS渲染 -->
</div>
</div>
<!-- 系统提示词 -->
<div class="mb-5">
<label class="form-label flex items-center">
<i class="fa fa-cog text-primary mr-2"></i>
系统提示词
<span class="ml-2 text-xs text-gray-400 font-normal">(可选,设置模型行为)</span>
</label>
<textarea id="systemPrompt" class="form-input" rows="3"
placeholder="例如:你是一个专业的技术助手,善于回答各类问题。请用简洁清晰的语言回答。"></textarea>
</div>
<!-- 用户提问 -->
<div class="mb-5">
<label class="form-label flex items-center">
<i class="fa fa-comment text-primary mr-2"></i>
用户提问
<span class="text-red-500 ml-1">*</span>
</label>
<textarea id="userQuestion" class="form-input" rows="4"
placeholder="请输入您想要对比的问题..."></textarea>
</div>
<!-- 推理参数设置 -->
<div class="mb-6">
<label class="form-label flex items-center mb-4">
<i class="fa fa-sliders text-primary mr-2"></i>
推理参数设置
</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<!-- Temperature -->
<div>
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600">Temperature</span>
<span id="tempValue" class="text-sm text-primary font-medium">0.7</span>
</div>
<input type="range" id="temperature" class="slider-thumb" min="0" max="2" step="0.1" value="0.7">
<div class="flex justify-between text-xs text-gray-400 mt-1">
<span>保守</span>
<span>随机</span>
</div>
</div>
<!-- Top-p -->
<div>
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600">Top-p</span>
<span id="topPValue" class="text-sm text-primary font-medium">0.9</span>
</div>
<input type="range" id="topP" class="slider-thumb" min="0" max="1" step="0.05" value="0.9">
<div class="flex justify-between text-xs text-gray-400 mt-1">
<span>集中</span>
<span>广泛</span>
</div>
</div>
<!-- Top-k -->
<div>
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600">Top-k</span>
<span id="topKValue" class="text-sm text-primary font-medium">50</span>
</div>
<input type="range" id="topK" class="slider-thumb" min="1" max="100" step="1" value="50">
<div class="flex justify-between text-xs text-gray-400 mt-1">
<span>精确</span>
<span>多样</span>
</div>
</div>
<!-- Max Tokens -->
<div>
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-600">Max Tokens</span>
<span id="maxTokensValue" class="text-sm text-primary font-medium">2048</span>
</div>
<input type="range" id="maxTokens" class="slider-thumb" min="256" max="4096" step="256" value="2048">
<div class="flex justify-between text-xs text-gray-400 mt-1">
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<!-- 按钮区域 -->
<div class="flex items-center justify-end pt-4 border-t border-gray-100">
<div class="flex items-center space-x-3">
<button type="button" onclick="goBack()" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg text-sm hover:bg-gray-300 transition-colors">
取消
</button>
<button type="button" id="startBtn" class="px-6 py-2 bg-primary text-white rounded-lg text-sm hover:bg-primary/90 transition-colors flex items-center disabled:opacity-50 disabled:cursor-not-allowed">
<i class="fa fa-play mr-2"></i>
开始提问
</button>
</div>
</div>
</form>
</div>
</div>
<script>
console.log('>>> model-compare-chat.html 脚本开始加载');
// API 基础地址 - 使用 config.yaml 中的 app.port (7861)
const getApiBase = () => {
const protocol = window.location.protocol;
const hostname = window.location.hostname;
return `${protocol}//${hostname}:7861/api`;
};
const API_BASE = getApiBase();
// 模拟模型数据(用于演示)
const mockModels = [
{ id: 1, name: 'GPT-4', type: 'LLM', description: 'OpenAI GPT-4 大语言模型' },
{ id: 2, name: 'Claude-3', type: 'LLM', description: 'Anthropic Claude-3 模型' },
{ id: 3, name: '文心一言', type: 'LLM', description: '百度文心一言大模型' },
{ id: 4, name: '通义千问', type: 'LLM', description: '阿里通义千问大模型' },
{ id: 5, name: 'ChatGLM', type: 'LLM', description: '智谱ChatGLM对话模型' },
{ id: 6, name: '星火认知', type: 'LLM', description: '讯飞星火认知大模型' }
];
let allModels = [];
let selectedModelIds = [];
let compareTaskId = null;
let compareTaskData = null;
// 模拟回复内容
const mockResponses = [
"这是一个基于深度学习的自然语言处理模型回答。我可以处理各种复杂的问题,包括代码编写、文本分析、知识问答等多种任务。我的训练数据涵盖了广泛的领域知识,能够提供准确和有用的信息。",
"您好!我是一个人工智能语言模型,很高兴为您服务。我可以帮助您解答问题、提供建议、进行创意写作等。如果您有任何需要,请随时告诉我,我会尽力提供帮助。",
"这是一个很有趣的问题!从技术角度来看,我们需要考虑多个因素:数据质量、模型架构、训练策略等。深度学习在近年来取得了巨大进展,但仍然存在一些挑战需要解决。",
"根据我的理解,这个问题涉及到以下几个方面:首先,需要明确问题的具体背景;其次,要分析相关的技术方案;最后,需要评估实施的成本和收益。建议您先收集更多信息再做决定。"
];
// 页面初始化函数
async function initPage() {
console.log('>>> chat initPage 开始执行');
console.log('document.readyState:', document.readyState);
try {
// 获取URL参数
const urlParams = new URLSearchParams(window.location.search);
compareTaskId = urlParams.get('id');
// 先加载对比任务数据获取已选模型ID列表
await loadCompareTask();
// 再加载模型列表
await loadModels();
// 确保两个都加载完成后再渲染已选模型
if (selectedModelIds.length > 0) {
renderSelectedModels();
}
} catch (e) {
console.error('加载数据失败:', e);
}
// 无论加载成功与否,都绑定事件
bindEvents();
}
// 立即初始化(支持通过 fetch 加载的页面)
// 延迟执行,确保 DOM 完全准备好
setTimeout(() => {
initPage().catch(err => console.error('initPage 执行失败:', err));
}, 50);
// 加载对比任务数据
async function loadCompareTask() {
if (!compareTaskId) {
updatePageTitle('请选择对比任务');
document.getElementById('startBtn').disabled = true;
showToast('请从列表页进入对比任务', 'warning');
return;
}
try {
const response = await fetch(`${API_BASE}/model-compare/${compareTaskId}`);
const result = await response.json();
if (result.code === 0) {
const taskData = Array.isArray(result.data)
? result.data.find(item => item && item.id == compareTaskId)
: result.data;
if (taskData) {
compareTaskData = taskData;
updatePageTitle(taskData.model_name || '模型对比');
const modelsField = taskData.models;
if (modelsField) {
try {
if (typeof modelsField === 'string') {
selectedModelIds = JSON.parse(modelsField);
} else if (Array.isArray(modelsField)) {
selectedModelIds = modelsField;
}
// 不在这里渲染,等 loadModels() 完成后再渲染
} catch (e) {
console.error('解析模型列表失败:', e);
}
}
} else {
console.warn('未找到对应任务数据');
}
} else {
showToast('加载对比任务失败: ' + result.message, 'error');
}
} catch (error) {
console.error('加载对比任务失败:', error);
showToast('加载对比任务失败', 'error');
}
}
// 更新页面标题
function updatePageTitle(title) {
const breadcrumb = document.getElementById('breadcrumbTask');
if (breadcrumb) {
breadcrumb.textContent = title;
}
}
// 加载模型列表
async function loadModels() {
try {
const response = await fetch(`${API_BASE}/model-manage`);
const result = await response.json();
if (result.code === 0) {
allModels = result.data || [];
}
} catch (error) {
console.error('加载模型失败:', error);
allModels = mockModels;
}
}
// 渲染已选模型标签
function renderSelectedModels() {
const container = document.getElementById('selectedModelsList');
const modelCount = document.getElementById('modelCount');
const allAvailableModels = [...allModels, ...mockModels];
modelCount.textContent = selectedModelIds.length;
if (selectedModelIds.length === 0) {
container.innerHTML = '<p class="text-gray-400 text-sm">暂无配置模型</p>';
return;
}
container.innerHTML = selectedModelIds.map((modelId, index) => {
// 支持 ID 数字或模型名称字符串匹配
const model = allAvailableModels.find(m =>
String(m.id) === String(modelId) || m.name === modelId
);
const colors = ['bg-blue-100 text-blue-700 border-blue-200', 'bg-green-100 text-green-700 border-green-200', 'bg-purple-100 text-purple-700 border-purple-200', 'bg-orange-100 text-orange-700 border-orange-200'];
const colorClass = colors[index % colors.length];
return `
<span class="model-tag px-3 py-1.5 rounded-lg text-sm font-medium flex items-center ${colorClass}">
${model?.name || modelId}
</span>
`;
}).join('');
}
// 绑定事件
function bindEvents() {
const container = document.getElementById('chatForm') || document.body;
const sliders = [
{ id: 'temperature', valueId: 'tempValue' },
{ id: 'topP', valueId: 'topPValue' },
{ id: 'topK', valueId: 'topKValue' },
{ id: 'maxTokens', valueId: 'maxTokensValue' }
];
sliders.forEach(({ id, valueId }) => {
const slider = container.querySelector('#' + id);
const valueDisplay = container.querySelector('#' + valueId);
if (slider && valueDisplay) {
slider.addEventListener('input', function() {
valueDisplay.textContent = this.value;
});
}
});
const startBtn = container.querySelector('#startBtn');
if (startBtn) startBtn.addEventListener('click', startGeneration);
}
// 开始生成回答 - 直接跳转到结果页面由结果页面调用API
function startGeneration() {
const userQuestion = document.getElementById('userQuestion').value.trim();
if (!userQuestion) {
showToast('请输入用户提问', 'warning');
return;
}
if (selectedModelIds.length === 0) {
showToast('该任务没有配置模型', 'warning');
return;
}
// 直接跳转到结果页面不在这里等待API结果
// 结果页面会自动调用API并显示加载状态
const taskName = compareTaskData?.model_name || '对比任务';
const encodedQuestion = encodeURIComponent(userQuestion);
window.location.href = `main.html?page=model-compare-result&taskId=${compareTaskId}&taskName=${encodeURIComponent(taskName)}&question=${encodedQuestion}&real=1`;
}
// 返回
function goBack() {
window.location.href = 'main.html?page=model-compare';
}
// 显示提示消息
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `fixed top-4 left-1/2 transform -translate-x-1/2 px-4 py-2 rounded-lg text-sm text-white z-50 transition-all duration-300 ${
type === 'warning' ? 'bg-yellow-500' :
type === 'error' ? 'bg-red-500' :
type === 'success' ? 'bg-green-500' : 'bg-primary'
}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translate(-50%, -20px)';
setTimeout(() => toast.remove(), 300);
}, 2000);
}
// 延迟函数
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
</script>
</body>
</html>