2026-07-10 16:45:55 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-07-16 11:03:25 +08:00
|
|
|
|
import { ref, reactive, computed, onMounted } from 'vue'
|
2026-07-10 16:45:55 +08:00
|
|
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
import PageCard from '@/components/PageCard.vue'
|
2026-07-16 11:03:25 +08:00
|
|
|
|
import { usePolling } from '@/composables/usePolling'
|
2026-07-10 16:45:55 +08:00
|
|
|
|
import { getCompare } from '@/api/modules/compare'
|
|
|
|
|
|
import type { CompareTask, LoadedModel } from '@/types'
|
|
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const taskId = route.params.id as string
|
|
|
|
|
|
|
|
|
|
|
|
const task = ref<CompareTask | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const form = reactive({
|
|
|
|
|
|
systemPrompt: '',
|
|
|
|
|
|
question: '',
|
|
|
|
|
|
temperature: 0.7,
|
|
|
|
|
|
topP: 0.9,
|
|
|
|
|
|
topK: 40,
|
|
|
|
|
|
maxTokens: 2048,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const loadedModels = computed<LoadedModel[]>(() => {
|
|
|
|
|
|
if (!task.value?.load_status) return []
|
|
|
|
|
|
try {
|
|
|
|
|
|
const ls =
|
|
|
|
|
|
typeof task.value.load_status === 'string'
|
|
|
|
|
|
? JSON.parse(task.value.load_status)
|
|
|
|
|
|
: task.value.load_status
|
|
|
|
|
|
return ls.loaded_models || []
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return []
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const allReady = computed(
|
|
|
|
|
|
() => loadedModels.value.length > 0 && loadedModels.value.every((m) => m.status === 'ready' || m.status === 'running'),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const isStarting = computed(() => loadedModels.value.some((m) => m.status === 'starting'))
|
|
|
|
|
|
|
|
|
|
|
|
async function loadTask() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
task.value = await getCompare(taskId)
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSubmit() {
|
|
|
|
|
|
if (!form.question.trim()) {
|
|
|
|
|
|
ElMessage.warning('请输入问题')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isStarting.value) {
|
|
|
|
|
|
ElMessage.warning('模型仍在启动中,请稍候')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 在新窗口打开结果页(原项目用 window.open)
|
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
|
taskId,
|
|
|
|
|
|
taskName: task.value?.model_name || task.value?.name || '',
|
|
|
|
|
|
question: form.question,
|
|
|
|
|
|
systemPrompt: form.systemPrompt,
|
|
|
|
|
|
temperature: String(form.temperature),
|
|
|
|
|
|
topP: String(form.topP),
|
|
|
|
|
|
topK: String(form.topK),
|
|
|
|
|
|
maxTokens: String(form.maxTokens),
|
|
|
|
|
|
})
|
|
|
|
|
|
const url = router.resolve(`/model-compare/result?${params.toString()}`).href
|
|
|
|
|
|
window.open(url, '_blank')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 11:03:25 +08:00
|
|
|
|
const { start: startPolling } = usePolling(loadTask, 5000, { immediate: false })
|
2026-07-10 16:45:55 +08:00
|
|
|
|
|
2026-07-16 11:03:25 +08:00
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
await loadTask()
|
|
|
|
|
|
startPolling()
|
2026-07-10 16:45:55 +08:00
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<PageCard title="模型对比配置">
|
|
|
|
|
|
<!-- 已启动模型 -->
|
|
|
|
|
|
<el-divider content-position="left">已启动模型</el-divider>
|
|
|
|
|
|
<div class="model-list">
|
|
|
|
|
|
<el-tag
|
|
|
|
|
|
v-for="(m, idx) in loadedModels"
|
|
|
|
|
|
:key="idx"
|
|
|
|
|
|
:type="m.status === 'ready' || m.status === 'running' ? 'success' : m.status === 'starting' ? 'warning' : 'danger'"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ m.model_name }} ({{ m.status }})
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
<span v-if="!loadedModels.length" class="empty-hint">暂无已启动模型</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 输入配置 -->
|
|
|
|
|
|
<el-divider content-position="left">对话配置</el-divider>
|
|
|
|
|
|
<el-form label-width="120px" style="max-width: 700px">
|
|
|
|
|
|
<el-form-item label="系统提示词">
|
|
|
|
|
|
<el-input v-model="form.systemPrompt" type="textarea" :rows="3" placeholder="可选" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="问题">
|
|
|
|
|
|
<el-input v-model="form.question" type="textarea" :rows="4" placeholder="请输入要对比的问题" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="Temperature">
|
|
|
|
|
|
<el-slider v-model="form.temperature" :min="0" :max="2" :step="0.1" show-input style="max-width: 500px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="Top-p">
|
|
|
|
|
|
<el-slider v-model="form.topP" :min="0" :max="1" :step="0.05" show-input style="max-width: 500px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="Top-k">
|
|
|
|
|
|
<el-slider v-model="form.topK" :min="1" :max="100" :step="1" show-input style="max-width: 500px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="Max Tokens">
|
|
|
|
|
|
<el-slider v-model="form.maxTokens" :min="256" :max="4096" :step="128" show-input style="max-width: 500px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" :disabled="!allReady || isStarting" @click="handleSubmit">
|
|
|
|
|
|
开始对比
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button @click="router.back()">返回</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</PageCard>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.model-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-hint {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|