mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-12 16:01:56 +08:00
feat: v0.8.2 — 15 fixes + 4 features + 3 improvements
Fixes: - Stop force-appending /v1 to API URLs (breaks Volcengine /v3 etc) - SSH upgrade: --unset-all + --add for 4 git insteadOf rules - Feishu: builtin detection, overlay→modal fix, select field, plugin version persistence - Docker: HTML response detection, Web mode guidance - Chat: runId dedup prevents duplicate messages - Cron: RPC params name→id - Channels: Gateway reload async (instant UI response), toggle cache invalidation - Linux: auto sudo for non-root npm installs (libc geteuid) - Control UI: dynamic hostname + auth token for remote access - npm: mirror fallback (npmmirror→npmjs.org) - QQBot: native binding friendly error message - Error diagnosis: SSH vs Git-not-installed, native binding detection Features: - About page: company info (武汉晴辰天下网络科技有限公司) - model-presets.js: shared module for models.js + assistant.js - Feishu: dual plugin support (builtin vs official @larksuiteoapi) - Assistant: provider preset quick-fill buttons Improvements: - Website: dynamic download links from latest.json + claw.qt.cool proxy - Linux deploy docs: upgrade guide, Gitee mirror, sudo notes - linux-deploy.sh: Gitee fallback + sudo npm + mirror retry
This commit is contained in:
@@ -49,6 +49,7 @@ let _currentAiBubble = null, _currentAiText = '', _currentAiImages = [], _curren
|
||||
let _isStreaming = false, _isSending = false, _messageQueue = [], _streamStartTime = 0
|
||||
let _lastRenderTime = 0, _renderPending = false, _lastHistoryHash = ''
|
||||
let _streamSafetyTimer = null, _unsubEvent = null, _unsubReady = null, _unsubStatus = null
|
||||
let _seenRunIds = new Set()
|
||||
let _pageActive = false
|
||||
let _errorTimer = null, _lastErrorMsg = null
|
||||
let _attachments = []
|
||||
@@ -886,6 +887,17 @@ function handleChatEvent(payload) {
|
||||
if (payload.sessionKey && payload.sessionKey !== _sessionKey && _sessionKey) return
|
||||
|
||||
const { state } = payload
|
||||
const runId = payload.runId
|
||||
|
||||
// 重复 run 过滤:跳过已完成的 runId 的后续事件(Gateway 可能对同一消息触发多个 run)
|
||||
if (runId && state === 'final' && _seenRunIds.has(runId)) {
|
||||
console.log('[chat] 跳过重复 final, runId:', runId)
|
||||
return
|
||||
}
|
||||
if (runId && state === 'delta' && _seenRunIds.has(runId) && !_isStreaming) {
|
||||
console.log('[chat] 跳过已完成 run 的 delta, runId:', runId)
|
||||
return
|
||||
}
|
||||
|
||||
if (state === 'delta') {
|
||||
const c = extractChatContent(payload.message)
|
||||
@@ -935,6 +947,14 @@ function handleChatEvent(payload) {
|
||||
const hasContent = finalText || _currentAiImages.length || _currentAiVideos.length || _currentAiAudios.length || _currentAiFiles.length
|
||||
// 忽略空 final(Gateway 会为一条消息触发多个 run,部分是空 final)
|
||||
if (!_currentAiBubble && !hasContent) return
|
||||
// 标记 runId 为已处理,防止重复
|
||||
if (runId) {
|
||||
_seenRunIds.add(runId)
|
||||
if (_seenRunIds.size > 200) {
|
||||
const first = _seenRunIds.values().next().value
|
||||
_seenRunIds.delete(first)
|
||||
}
|
||||
}
|
||||
showTyping(false)
|
||||
// 如果流式阶段没有创建 bubble,从 final message 中提取
|
||||
if (!_currentAiBubble && hasContent) {
|
||||
|
||||
Reference in New Issue
Block a user