fix: keep model activity alive and switch new chats
This commit is contained in:
parent
427a1f7ea8
commit
7b735e252f
@ -105,6 +105,16 @@ function debugLog(...args) {
|
||||
}
|
||||
debugLog(...args);
|
||||
}
|
||||
// 临时排查对话切换问题的调试输出
|
||||
const TRACE_CONV = true;
|
||||
const traceLog = (...args) => {
|
||||
if (!TRACE_CONV) return;
|
||||
try {
|
||||
console.log('[conv-trace]', ...args);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const appOptions = {
|
||||
data() {
|
||||
@ -136,6 +146,8 @@ const appOptions = {
|
||||
skipConversationHistoryReload: false,
|
||||
_scrollListenerReady: false,
|
||||
historyLoading: false,
|
||||
historyLoadingFor: null,
|
||||
historyLoadSeq: 0,
|
||||
mobileViewportQuery: null,
|
||||
modeMenuOpen: false,
|
||||
conversationListRequestSeq: 0,
|
||||
@ -334,6 +346,14 @@ const appOptions = {
|
||||
immediate: false,
|
||||
handler(newValue, oldValue) {
|
||||
debugLog('currentConversationId 变化', { oldValue, newValue, skipConversationHistoryReload: this.skipConversationHistoryReload });
|
||||
traceLog('watch:currentConversationId', {
|
||||
oldValue,
|
||||
newValue,
|
||||
skipConversationHistoryReload: this.skipConversationHistoryReload,
|
||||
historyLoading: this.historyLoading,
|
||||
historyLoadingFor: this.historyLoadingFor,
|
||||
historyLoadSeq: this.historyLoadSeq
|
||||
});
|
||||
this.logMessageState('watch:currentConversationId', { oldValue, newValue, skipConversationHistoryReload: this.skipConversationHistoryReload });
|
||||
if (!newValue || typeof newValue !== 'string' || newValue.startsWith('temp_')) {
|
||||
return;
|
||||
@ -1295,12 +1315,15 @@ const appOptions = {
|
||||
this.loadingMoreConversations = false;
|
||||
},
|
||||
|
||||
async loadConversation(conversationId) {
|
||||
async loadConversation(conversationId, options = {}) {
|
||||
const force = Boolean(options.force);
|
||||
debugLog('加载对话:', conversationId);
|
||||
this.logMessageState('loadConversation:start', { conversationId });
|
||||
traceLog('loadConversation:start', { conversationId, currentConversationId: this.currentConversationId, force });
|
||||
this.logMessageState('loadConversation:start', { conversationId, force });
|
||||
|
||||
if (conversationId === this.currentConversationId) {
|
||||
if (!force && conversationId === this.currentConversationId) {
|
||||
debugLog('已是当前对话,跳过加载');
|
||||
traceLog('loadConversation:skip-same', { conversationId });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1313,6 +1336,7 @@ const appOptions = {
|
||||
|
||||
if (result.success) {
|
||||
debugLog('对话加载API成功:', result);
|
||||
traceLog('loadConversation:api-success', { conversationId, title: result.title });
|
||||
|
||||
// 2. 更新当前对话信息
|
||||
this.skipConversationHistoryReload = true;
|
||||
@ -1331,6 +1355,10 @@ const appOptions = {
|
||||
await this.fetchAndDisplayHistory();
|
||||
this.fetchConversationTokenStatistics();
|
||||
this.updateCurrentContextTokens();
|
||||
traceLog('loadConversation:after-history', {
|
||||
conversationId,
|
||||
messagesLen: Array.isArray(this.messages) ? this.messages.length : 'n/a'
|
||||
});
|
||||
|
||||
} else {
|
||||
console.error('对话加载失败:', result.message);
|
||||
@ -1342,6 +1370,7 @@ const appOptions = {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载对话异常:', error);
|
||||
traceLog('loadConversation:error', { conversationId, error: error?.message || String(error) });
|
||||
this.uiPushToast({
|
||||
title: '加载对话异常',
|
||||
message: error.message || String(error),
|
||||
@ -1365,23 +1394,28 @@ const appOptions = {
|
||||
// 关键功能:获取并显示历史对话内容
|
||||
// ==========================================
|
||||
async fetchAndDisplayHistory() {
|
||||
if (this.historyLoading) {
|
||||
debugLog('历史消息正在加载,跳过重复请求');
|
||||
return;
|
||||
}
|
||||
this.historyLoading = true;
|
||||
try {
|
||||
debugLog('开始获取历史对话内容...');
|
||||
this.logMessageState('fetchAndDisplayHistory:start', { conversationId: this.currentConversationId });
|
||||
|
||||
if (!this.currentConversationId || this.currentConversationId.startsWith('temp_')) {
|
||||
const targetConversationId = this.currentConversationId;
|
||||
if (!targetConversationId || targetConversationId.startsWith('temp_')) {
|
||||
debugLog('没有当前对话ID,跳过历史加载');
|
||||
return;
|
||||
}
|
||||
|
||||
// 若同一对话正在加载,直接复用;若是切换对话则允许并发但后来的请求会赢
|
||||
if (this.historyLoading && this.historyLoadingFor === targetConversationId) {
|
||||
debugLog('同一对话历史正在加载,跳过重复请求');
|
||||
return;
|
||||
}
|
||||
|
||||
const loadSeq = ++this.historyLoadSeq;
|
||||
this.historyLoading = true;
|
||||
this.historyLoadingFor = targetConversationId;
|
||||
try {
|
||||
debugLog('开始获取历史对话内容...');
|
||||
this.logMessageState('fetchAndDisplayHistory:start', { conversationId: this.currentConversationId });
|
||||
|
||||
try {
|
||||
// 使用专门的API获取对话消息历史
|
||||
const messagesResponse = await fetch(`/api/conversations/${this.currentConversationId}/messages`);
|
||||
const messagesResponse = await fetch(`/api/conversations/${targetConversationId}/messages`);
|
||||
|
||||
if (!messagesResponse.ok) {
|
||||
console.warn('无法获取消息历史,尝试备用方法');
|
||||
@ -1401,6 +1435,12 @@ const appOptions = {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果在等待期间用户已切换到其他对话,则丢弃结果
|
||||
if (loadSeq !== this.historyLoadSeq || this.currentConversationId !== targetConversationId) {
|
||||
debugLog('检测到对话已切换,丢弃过期的历史加载结果');
|
||||
return;
|
||||
}
|
||||
|
||||
const messagesData = await messagesResponse.json();
|
||||
debugLog('获取到消息数据:', messagesData);
|
||||
|
||||
@ -1445,7 +1485,11 @@ const appOptions = {
|
||||
this.logMessageState('fetchAndDisplayHistory:error-cleared');
|
||||
}
|
||||
} finally {
|
||||
// 仅在本次加载仍是最新请求时清除 loading 状态
|
||||
if (loadSeq === this.historyLoadSeq) {
|
||||
this.historyLoading = false;
|
||||
this.historyLoadingFor = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1692,6 +1736,10 @@ const appOptions = {
|
||||
|
||||
async createNewConversation() {
|
||||
debugLog('创建新对话...');
|
||||
traceLog('createNewConversation:start', {
|
||||
currentConversationId: this.currentConversationId,
|
||||
convCount: Array.isArray(this.conversations) ? this.conversations.length : 'n/a'
|
||||
});
|
||||
this.logMessageState('createNewConversation:start');
|
||||
|
||||
try {
|
||||
@ -1711,6 +1759,7 @@ const appOptions = {
|
||||
if (result.success) {
|
||||
const newConversationId = result.conversation_id;
|
||||
debugLog('新对话创建成功:', newConversationId);
|
||||
traceLog('createNewConversation:created', { newConversationId });
|
||||
|
||||
// 在本地列表插入占位,避免等待刷新
|
||||
const placeholder = {
|
||||
@ -1726,11 +1775,20 @@ const appOptions = {
|
||||
];
|
||||
|
||||
// 直接加载新对话,确保状态一致
|
||||
await this.loadConversation(newConversationId);
|
||||
// 如果 socket 事件已把 currentConversationId 设置为新ID,则强制加载一次以同步状态
|
||||
await this.loadConversation(newConversationId, { force: true });
|
||||
traceLog('createNewConversation:after-load', {
|
||||
newConversationId,
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
|
||||
// 刷新对话列表获取最新统计
|
||||
this.conversationsOffset = 0;
|
||||
await this.loadConversationsList();
|
||||
traceLog('createNewConversation:after-refresh', {
|
||||
newConversationId,
|
||||
conversationsLen: Array.isArray(this.conversations) ? this.conversations.length : 'n/a'
|
||||
});
|
||||
} else {
|
||||
console.error('创建对话失败:', result.message);
|
||||
this.uiPushToast({
|
||||
|
||||
@ -591,6 +591,7 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
// 监听对话变更事件
|
||||
ctx.socket.on('conversation_changed', (data) => {
|
||||
socketLog('对话已切换:', data);
|
||||
console.log('[conv-trace] socket:conversation_changed', data);
|
||||
ctx.currentConversationId = data.conversation_id;
|
||||
ctx.currentConversationTitle = data.title || '';
|
||||
ctx.promoteConversationToTop(data.conversation_id);
|
||||
@ -618,6 +619,7 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
return;
|
||||
}
|
||||
const convId = data.conversation_id;
|
||||
console.log('[conv-trace] socket:conversation_resolved', data);
|
||||
ctx.currentConversationId = convId;
|
||||
if (data.title) {
|
||||
ctx.currentConversationTitle = data.title;
|
||||
|
||||
@ -2278,8 +2278,29 @@ def handle_message(data):
|
||||
"""发送消息到客户端"""
|
||||
socketio.emit(event_type, data, room=client_sid)
|
||||
|
||||
# 模型活动事件:用于刷新“在线”心跳(回复/工具调用都算活动)
|
||||
activity_events = {
|
||||
'ai_message_start', 'thinking_start', 'thinking_chunk', 'thinking_end',
|
||||
'text_start', 'text_chunk', 'text_end',
|
||||
'tool_hint', 'tool_preparing', 'tool_start', 'update_action',
|
||||
'append_payload', 'modify_payload', 'system_message',
|
||||
'task_complete'
|
||||
}
|
||||
last_model_activity = 0.0
|
||||
|
||||
def send_with_activity(event_type, data):
|
||||
"""模型产生输出或调用工具时刷新活跃时间,防止长回复被误判下线。"""
|
||||
nonlocal last_model_activity
|
||||
if event_type in activity_events:
|
||||
now = time.time()
|
||||
# 轻量节流:1 秒内多次事件只记一次
|
||||
if now - last_model_activity >= 1.0:
|
||||
record_user_activity(username)
|
||||
last_model_activity = now
|
||||
send_to_client(event_type, data)
|
||||
|
||||
# 传递客户端ID
|
||||
socketio.start_background_task(process_message_task, terminal, message, send_to_client, client_sid)
|
||||
socketio.start_background_task(process_message_task, terminal, message, send_with_activity, client_sid)
|
||||
|
||||
|
||||
@socketio.on('client_chunk_log')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user