feat(chat): 增强对话中断处理,优化用户体验

- 在 chat_agent 函数中添加对客户端主动中断连接的处理,尝试保存已生成的部分内容
- 更新前端 AgentChatComponent.vue,新增发送或中断功能,确保用户可以中断对话生成并刷新消息历史
- 优化状态处理,确保中断状态下的消息历史刷新逻辑正常工作
This commit is contained in:
Wenjie Zhang 2025-10-06 21:44:17 +08:00
parent b90a796172
commit 23f8fed679
2 changed files with 50 additions and 4 deletions

View File

@ -322,6 +322,26 @@ async def chat_agent(
conv_mgr=conv_manager,
config_dict=langgraph_config,
)
except (asyncio.CancelledError, ConnectionError) as e:
# 客户端主动中断连接,尝试保存已生成的部分内容
logger.info(f"Client disconnected for thread {thread_id}: {e}")
try:
if conversation:
langgraph_config = {"configurable": {"thread_id": thread_id, "user_id": user_id}}
await save_messages_from_langgraph_state(
agent_instance=agent,
conversation=conversation,
conv_mgr=conv_manager,
config_dict=langgraph_config,
)
except Exception as save_error:
logger.error(f"Error saving partial messages after disconnect: {save_error}")
# 通知前端中断(可能发送不到,但用于一致性)
try:
yield make_chunk(status="interrupted", message="对话已中断", meta=meta)
except Exception:
pass
return
except Exception as e:
logger.error(f"Error streaming messages: {e}, {traceback.format_exc()}")
yield make_chunk(message=f"Error streaming messages: {e}", status="error")

View File

@ -71,9 +71,9 @@
v-model="userInput"
:is-loading="isProcessing"
:disabled="!currentAgent"
:send-button-disabled="!userInput || !currentAgent || isProcessing"
:send-button-disabled="(!userInput || !currentAgent) && !isProcessing"
:placeholder="'输入问题...'"
@send="handleSendMessage"
@send="handleSendOrStop"
@keydown="handleKeyDown"
/>
@ -131,9 +131,9 @@
v-model="userInput"
:is-loading="isProcessing"
:disabled="!currentAgent"
:send-button-disabled="!userInput || !currentAgent || isProcessing"
:send-button-disabled="(!userInput || !currentAgent) && !isProcessing"
:placeholder="'输入问题...'"
@send="handleSendMessage"
@send="handleSendOrStop"
@keydown="handleKeyDown"
/>
<div class="bottom-actions">
@ -416,6 +416,11 @@ const _processStreamChunk = (chunk, threadId) => {
fetchThreadMessages({ agentId: currentAgentId.value, threadId: threadId });
resetOnGoingConv(threadId);
break;
case 'interrupted':
//
fetchThreadMessages({ agentId: currentAgentId.value, threadId: threadId });
resetOnGoingConv(threadId);
break;
}
};
@ -726,6 +731,27 @@ const handleSendMessage = async () => {
}
};
//
const handleSendOrStop = async () => {
const threadId = currentChatId.value;
const threadState = getThreadState(threadId);
if (isProcessing.value && threadState && threadState.streamAbortController) {
//
threadState.streamAbortController.abort();
//
try {
await fetchThreadMessages({ agentId: currentAgentId.value, threadId: threadId });
message.info('已中断对话生成');
} catch (error) {
console.error('刷新消息历史失败:', error);
message.info('已中断对话生成');
}
return;
}
await handleSendMessage();
};
// ==================== UI HANDLERS ====================
const handleRenameChat = () => {
if (!currentChatId.value || !currentThread.value) {