聊天组件优化

This commit is contained in:
shunfeng.zhou 2025-04-15 01:23:41 +08:00
parent 1fafb3a2bf
commit 5a58d1c298
3 changed files with 787 additions and 475 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,10 @@ import './styles/antdx-override.scss';
// 设置全局VSCode API
declare global {
interface Window {
vscodeApi: any;
__VSCODE_API_INITIALIZED__: boolean;
vscodeApi?: {
postMessage: (message: any) => void;
};
__VSCODE_API_INITIALIZED__?: boolean;
}
}

View File

@ -1,16 +1,16 @@
import { defineStore } from 'pinia';
import { ref, reactive } from 'vue';
import { ref, reactive, nextTick } from 'vue';
import { v4 } from 'uuid';
// 定义消息类型
export interface ChatMessage {
id: string;
role: string;
content: string;
role?: 'user' | 'assistant' | 'system';
timestamp?: number;
createAt?: Date;
pending?: boolean;
error?: boolean;
createAt?: Date;
}
export interface SendMessageOptions {
@ -598,14 +598,20 @@ export class WebSocketService {
* @param content
* @param modelId ID
* @param conversationId ID
* @param customAIMessageId AI消息ID ()
* @returns
*/
public sendChatMessage(
content: string,
modelId?: string,
conversationId?: string
conversationId?: string,
customAIMessageId?: string
): Promise<ChatMessage> {
return new Promise<ChatMessage>((resolve, reject) => {
// 确保设置加载状态
this.isLoading = true;
this.isWaitingForReply = true;
// 创建用户消息对象
const userMsg: ChatMessage = {
id: v4(),
@ -617,10 +623,12 @@ export class WebSocketService {
// 将用户消息添加到消息列表
this.addMessageToList(userMsg);
// 创建请求ID并实例化AI响应消息
// 创建请求ID
const requestId = this.generateRequestId();
// 创建AI响应消息对象
const aiMsg: ChatMessage = {
id: v4(),
id: customAIMessageId || v4(), // 使用自定义ID或生成新的UUID
role: "assistant",
content: "正在思考中...",
pending: true,
@ -630,8 +638,11 @@ export class WebSocketService {
// 将AI消息添加到消息列表
this.addMessageToList(aiMsg);
// 明确触发消息更新事件
this.emit(WebSocketEvent.CHAT_UPDATE);
// 设置超时处理如果10秒内没有收到真正的响应重置loading状态
setTimeout(() => {
const timeoutId = setTimeout(() => {
if (aiMsg.pending) {
this.logger(`10秒后未收到真正的AI响应重置loading状态`);
aiMsg.pending = false;
@ -646,13 +657,28 @@ export class WebSocketService {
// 注册响应处理器
this.requestHandlers.set(requestId, (data: any) => {
try {
// 首先清除超时定时器
clearTimeout(timeoutId);
this.logger(`收到AI响应: ${JSON.stringify(data)}`);
// 检查是否是取消响应
if (data.canceled) {
this.logger('请求已被用户取消');
aiMsg.content = data.msg || "用户已取消生成";
aiMsg.pending = false;
this.updateMessage(aiMsg);
this.isLoading = false;
this.isWaitingForReply = false;
this.emit(WebSocketEvent.CHAT_UPDATE);
resolve(aiMsg);
return;
}
// 根据响应更新AI消息
const responseContent = data.msg || data.message || data.answer || data.content || "";
// 确保响应内容不是用户输入的内容副本
if (responseContent !== content) {
// 无论内容如何都更新AI消息
aiMsg.content = responseContent;
aiMsg.pending = false;
@ -670,10 +696,6 @@ export class WebSocketService {
// 标记加载完成
this.isLoading = false;
this.isWaitingForReply = false;
} else {
// 如果响应内容与用户输入相同,保持"正在思考中..."状态
this.logger(`收到的响应内容与用户输入相同等待真正的AI响应`);
}
// 解决Promise
resolve(aiMsg);
@ -683,6 +705,12 @@ export class WebSocketService {
aiMsg.error = true;
aiMsg.pending = false;
this.updateMessage(aiMsg);
// 确保重置状态
this.isLoading = false;
this.isWaitingForReply = false;
this.emit(WebSocketEvent.CHAT_UPDATE);
reject(error);
}
});
@ -697,7 +725,29 @@ export class WebSocketService {
};
this.logger(`发送聊天请求: ${JSON.stringify(request)}`);
// 尝试发送请求
try {
this.sendRequest(request);
} catch (error) {
// 处理发送请求时的错误
clearTimeout(timeoutId);
this.logger(`发送请求失败: ${error}`);
aiMsg.content = `发送请求失败: ${error instanceof Error ? error.message : '未知错误'}`;
aiMsg.error = true;
aiMsg.pending = false;
this.updateMessage(aiMsg);
// 确保重置状态
this.isLoading = false;
this.isWaitingForReply = false;
this.emit(WebSocketEvent.CHAT_UPDATE);
// 从处理器映射中移除
this.requestHandlers.delete(requestId);
reject(error);
}
});
}
@ -951,6 +1001,96 @@ export class WebSocketService {
this.emit(WebSocketEvent.MESSAGE, errorMessage);
}
}
/**
*
* @returns
*/
public cancelCurrentRequest(): boolean {
this.logger('正在尝试取消当前请求');
// 找到最新的请求ID
if (this.requestHandlers.size === 0) {
this.logger('没有活跃的请求处理程序,无法取消');
// 无论如何,确保重置状态
this.isLoading = false;
this.isWaitingForReply = false;
// 清理任何挂起的消息状态
const pendingMessage = this.messages.find(m => m.pending);
if (pendingMessage) {
pendingMessage.pending = false;
pendingMessage.content = "已结束";
this.updateMessage(pendingMessage);
this.emit(WebSocketEvent.CHAT_UPDATE);
}
return false;
}
// 获取所有请求ID并找到最大的最新的
const requestIds = Array.from(this.requestHandlers.keys());
const latestRequestId = Math.max(...requestIds);
this.logger(`找到最新的请求ID: ${latestRequestId},正在取消`);
// 移除对应的请求处理程序
const handler = this.requestHandlers.get(latestRequestId);
if (handler) {
// 调用处理程序,传递取消状态
handler({ canceled: true, msg: "请求已取消" });
this.requestHandlers.delete(latestRequestId);
// 尝试发送取消命令如果API支持
try {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({
cmd: "cancel_request",
request_id: latestRequestId
}));
this.logger(`已发送取消命令请求ID: ${latestRequestId}`);
}
} catch (error) {
this.logger(`发送取消命令失败: ${error}`);
}
// 清除所有其他请求处理程序
for (const rid of requestIds) {
if (rid !== latestRequestId) {
this.requestHandlers.delete(rid);
}
}
// 重置状态
this.isLoading = false;
this.isWaitingForReply = false;
// 重置任何挂起的消息
for (const msg of this.messages) {
if (msg.pending) {
msg.pending = false;
if (msg.content === "正在思考中..." || !msg.content) {
msg.content = "已取消";
}
this.updateMessage(msg);
}
}
// 通知UI更新
this.emit(WebSocketEvent.CHAT_UPDATE);
return true;
}
this.logger(`未找到对应的请求处理程序,取消失败`);
// 无论如何,重置状态
this.isLoading = false;
this.isWaitingForReply = false;
this.emit(WebSocketEvent.CHAT_UPDATE);
return false;
}
}
// 创建聊天状态管理
@ -966,7 +1106,9 @@ export const useChatStore = defineStore('chat', () => {
errorMessage: '',
availableModels: [] as string[],
currentModel: '',
loadingModels: false
loadingModels: false,
lastInitializedConfig: '',
senderKey: Date.now(), // 添加一个键用于强制更新sender组件
});
// 初始化WebSocket服务
@ -1226,7 +1368,7 @@ export const useChatStore = defineStore('chat', () => {
}
// 发送消息
async function sendMessage(content: string): Promise<string> {
async function sendMessage(content: string, model?: string, customAIMessageId?: string): Promise<string> {
// 记录函数调用到VSCode日志
const logToVSCode = (message: string) => {
try {
@ -1266,19 +1408,22 @@ export const useChatStore = defineStore('chat', () => {
return '';
}
// 使用传入的模型或当前模型
const selectedModel = model || chatState.currentModel;
// 确保有当前模型
if (!chatState.currentModel && chatState.availableModels.length > 0) {
if (!selectedModel && chatState.availableModels.length > 0) {
console.log('[ChatStore] 自动选择模型', chatState.availableModels[0]);
logToVSCode(`自动选择模型: ${chatState.availableModels[0]}`);
chatState.currentModel = chatState.availableModels[0];
} else if (!chatState.currentModel) {
} else if (!selectedModel) {
console.error('[ChatStore] 没有可用模型');
logToVSCode('没有可用模型');
throw new Error('没有可用的AI模型');
}
console.log(`[ChatStore] 准备发送消息到WebSocket使用模型 ${chatState.currentModel}`);
logToVSCode(`准备发送消息到WebSocket使用模型 ${chatState.currentModel}`);
console.log(`[ChatStore] 准备发送消息到WebSocket使用模型 ${selectedModel}`);
logToVSCode(`准备发送消息到WebSocket使用模型 ${selectedModel}`);
chatState.loading = true;
try {
@ -1295,8 +1440,8 @@ export const useChatStore = defineStore('chat', () => {
throw new Error('WebSocket服务方法不可用');
}
logToVSCode('调用sendChatMessage直接...');
wsService.value.sendChatMessage(content, chatState.currentModel);
logToVSCode(`调用sendChatMessage直接...${customAIMessageId ? '使用自定义ID: ' + customAIMessageId : '生成新ID'}`);
wsService.value.sendChatMessage(content, selectedModel, customAIMessageId);
logToVSCode('sendChatMessage调用完成已发送消息');
// 不立即重置loading状态等待真正的响应通过事件更新
@ -1344,7 +1489,7 @@ export const useChatStore = defineStore('chat', () => {
chatState.currentModel = model;
}
// 清空消息
// 清空消息列表(上面有一个同名函数需要删除)
function clearMessages() {
chatState.messages = [];
}
@ -1358,13 +1503,153 @@ export const useChatStore = defineStore('chat', () => {
}
}
// 设置加载状态
function setLoading(status: boolean) {
// 如果状态没有变化,直接返回
if (chatState.loading === status) {
return;
}
console.log(`[ChatStore] 设置loading状态: ${status}`);
try {
if (window.vscodeApi) {
window.vscodeApi.postMessage({
type: 'log',
message: `[ChatStore] 设置loading状态: ${status}`
});
}
} catch (error) {
console.error('向VSCode发送日志失败', error);
}
// 直接设置加载状态
chatState.loading = status;
// 同步更新WebSocketService的状态
if (wsService.value) {
wsService.value.isLoading = status;
wsService.value.isWaitingForReply = status;
}
// 如果loading状态被重置为false检查是否有任何未完成的消息
// 但避免遍历和修改所有消息只处理最后一条AI消息
if (!status) {
const aiMessages = chatState.messages.filter(m => m.role === 'assistant' && m.pending);
if (aiMessages.length > 0) {
// 只处理最后一条pending的AI消息
const lastPendingMessage = aiMessages[aiMessages.length - 1];
const index = chatState.messages.indexOf(lastPendingMessage);
if (index !== -1) {
// 创建消息的副本以确保响应式更新
const updatedMessage = { ...chatState.messages[index] };
updatedMessage.pending = false;
// 更新消息
chatState.messages[index] = updatedMessage;
}
}
}
}
// 设置连接状态
function setConnectionStatus(status: 'connecting' | 'connected' | 'disconnected' | 'error') {
chatState.connectionStatus = status;
}
// 设置错误消息
function setErrorMessage(message: string) {
chatState.errorMessage = message;
}
// 设置模型列表加载状态
function setLoadingModels(status: boolean) {
chatState.loadingModels = status;
}
// 更新消息列表
function updateMessages(messages: ChatMessage[]) {
chatState.messages = messages;
}
// 添加消息
function addMessage(message: ChatMessage) {
chatState.messages.push(message);
}
// 添加消息到列表
function addMessageToList(message: ChatMessage) {
chatState.messages.push(message);
}
// 更新现有消息
function updateMessage(index: number, message: ChatMessage) {
if (index >= 0 && index < chatState.messages.length) {
chatState.messages[index] = message;
}
}
// 取消当前生成
function cancelGeneration() {
console.log('[ChatStore] 尝试取消当前生成');
if (!wsService.value) {
console.error('[ChatStore] WebSocket服务未初始化无法取消生成');
return false;
}
// 调用WebSocketService的取消方法
const canceled = wsService.value.cancelCurrentRequest();
// 无论是否成功取消都重置UI状态
chatState.loading = false;
// 如果有正在显示的pendingMessage更新其状态
const pendingMessage = chatState.messages.find(m => m.pending);
if (pendingMessage) {
const index = chatState.messages.indexOf(pendingMessage);
if (index !== -1) {
// 更新消息内容
pendingMessage.content = canceled ? "用户已取消生成" : pendingMessage.content;
pendingMessage.pending = false;
chatState.messages[index] = pendingMessage;
}
}
return canceled;
}
// 获取WebSocketService实例
function getWebSocketService() {
return wsService.value;
}
// 发送聊天消息 - 简化版本,主要调用 sendMessage 但返回更清晰的结果
async function sendChatMessage(content: string, model?: string, customAIMessageId?: string): Promise<string> {
console.log('[ChatStore] sendChatMessage called', { content, model, customAIMessageId });
return await sendMessage(content, model, customAIMessageId);
}
return {
chatState,
initWebSocketService,
sendMessage,
sendChatMessage,
refreshModels,
setCurrentModel,
clearMessages,
reconnect
reconnect,
// 添加新的actions
setLoading,
setConnectionStatus,
setErrorMessage,
setLoadingModels,
updateMessages,
addMessage,
addMessageToList,
updateMessage,
cancelGeneration,
getWebSocketService
};
});