refactor: 推理对比超时与打字机展示改进
抽取 withTimeout 替代 Promise.race 超时控制,对比结果新增打字机逐字渲染与清理,推理聊天参数与列表类型同步收敛。
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
import { ref, reactive, nextTick, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import MarkdownView from '@/components/MarkdownView.vue'
|
||||
@@ -34,6 +34,7 @@ const temperature = ref(0.7)
|
||||
const top_p = ref(0.95)
|
||||
const maxTokens = ref(2048)
|
||||
const contentRef = ref<HTMLElement>()
|
||||
let activeAssistant: ChatMessage | null = null
|
||||
/** 设置面板抽屉 */
|
||||
const showSettings = ref(false)
|
||||
|
||||
@@ -98,8 +99,8 @@ async function handleSend() {
|
||||
return
|
||||
}
|
||||
|
||||
// 监听流式 message 变化,同步到 assistantMsg
|
||||
const watchStop = watchMessage(assistantMsg)
|
||||
// 流式状态变化时只同步当前回复,避免固定定时器空转。
|
||||
activeAssistant = assistantMsg
|
||||
|
||||
await send({
|
||||
port: target.port,
|
||||
@@ -118,7 +119,7 @@ async function handleSend() {
|
||||
assistantMsg.isThinking = false
|
||||
assistantMsg.isStreaming = false
|
||||
assistantMsg.done = true
|
||||
watchStop()
|
||||
activeAssistant = null
|
||||
reset()
|
||||
await nextTick()
|
||||
scrollToBottom()
|
||||
@@ -149,17 +150,24 @@ async function mockReply(assistantMsg: ChatMessage, question: string) {
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
/** 轮询同步流式状态到展示消息 */
|
||||
function watchMessage(assistantMsg: ChatMessage) {
|
||||
const timer = setInterval(() => {
|
||||
assistantMsg.content = message.value.displayContent
|
||||
assistantMsg.think = message.value.thinkContent
|
||||
assistantMsg.isThinking = message.value.isThinking
|
||||
if (message.value.done) clearInterval(timer)
|
||||
watch(
|
||||
() => [
|
||||
message.value.displayContent,
|
||||
message.value.thinkContent,
|
||||
message.value.isThinking,
|
||||
message.value.done,
|
||||
] as const,
|
||||
async ([content, think, isThinking, done]) => {
|
||||
if (!activeAssistant) return
|
||||
activeAssistant.content = content
|
||||
activeAssistant.think = think
|
||||
activeAssistant.isThinking = isThinking
|
||||
activeAssistant.isStreaming = !done
|
||||
await nextTick()
|
||||
scrollToBottom()
|
||||
}, 80)
|
||||
return () => clearInterval(timer)
|
||||
}
|
||||
},
|
||||
{ flush: 'post' },
|
||||
)
|
||||
|
||||
function scrollToBottom() {
|
||||
if (contentRef.value) {
|
||||
@@ -168,6 +176,7 @@ function scrollToBottom() {
|
||||
}
|
||||
|
||||
function handleNewChat() {
|
||||
activeAssistant = null
|
||||
messages.value = []
|
||||
reset()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import DataTablePage from '@/components/DataTablePage.vue'
|
||||
import { usePolling } from '@/composables/usePolling'
|
||||
import {
|
||||
getCompareList,
|
||||
deleteCompare,
|
||||
@@ -17,7 +18,7 @@ const router = useRouter()
|
||||
|
||||
const loading = ref(false)
|
||||
const dataList = ref<CompareTask[]>([])
|
||||
let refreshTimer: ReturnType<typeof setInterval> | null = null
|
||||
let delayedRefreshTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
async function loadData(silent = false) {
|
||||
if (!silent) {
|
||||
@@ -80,7 +81,8 @@ function parseModelNames(row: any): string[] {
|
||||
async function handleLoad(row: any) {
|
||||
await loadCompare(row.id)
|
||||
ElMessage.info('正在加载模型,请稍候...')
|
||||
setTimeout(loadData, 1000)
|
||||
if (delayedRefreshTimer) clearTimeout(delayedRefreshTimer)
|
||||
delayedRefreshTimer = setTimeout(loadData, 1000)
|
||||
}
|
||||
|
||||
/** 卸载推理任务 */
|
||||
@@ -112,13 +114,15 @@ function startChat(row: any) {
|
||||
router.push(`/model-inference/chat/${row.id}`)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
refreshTimer = setInterval(() => loadData(true), 3000)
|
||||
const { start: startPolling } = usePolling(() => loadData(true), 3000, { immediate: false })
|
||||
|
||||
onMounted(async () => {
|
||||
await loadData()
|
||||
startPolling()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) clearInterval(refreshTimer)
|
||||
if (delayedRefreshTimer) clearTimeout(delayedRefreshTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user