聊天组件优化
This commit is contained in:
parent
1fafb3a2bf
commit
5a58d1c298
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user