2025-04-13 14:24:48 +08:00

917 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defineStore } from 'pinia';
import { ref, reactive } from 'vue';
// 定义消息类型
export interface ChatMessage {
id: string;
content: string;
role: 'user' | 'assistant';
createAt: Date;
}
// WebSocket事件类型
export enum WebSocketEvent {
CONNECTED = 'connected',
DISCONNECTED = 'disconnected',
MESSAGE = 'message',
ERROR = 'error',
MODEL_LIST = 'model_list'
}
// 定义VSCode API接口
let vscode: any = undefined;
try {
vscode = acquireVsCodeApi();
} catch (error) {
// 在开发环境下可能没有VSCode API
console.warn('VSCode API不可用可能是在浏览器开发环境中运行');
}
// WebSocket服务类 - 浏览器环境下实现
export class WebSocketService {
private socket: WebSocket | null = null;
private requestMap = new Map<number, (response: any) => void>();
private nextRequestId = 1;
private reconnectAttempts = 0;
private reconnectTimer: number | null = null;
private readonly MAX_RECONNECT_ATTEMPTS = 5;
private readonly RECONNECT_DELAY = 2000;
private isReconnecting = false;
private pingInterval: number | null = null;
public isConnected = false;
private eventListeners: Map<string, Array<(data?: any) => void>> = new Map();
constructor(private apiUrl: string, private apiKey: string, private logger: (message: string) => void = console.log) {}
/**
* 连接到WebSocket服务器
*/
public connect(): void {
if (this.socket?.readyState === WebSocket.OPEN) {
this.logger('[WebSocketService] 已连接');
return;
}
try {
this.logger(`[WebSocketService] 尝试连接到: ${this.apiUrl}`);
// 检查URL格式
let wsUrl = this.apiUrl;
// 确保URL以/ws结尾但避免重复
if (!wsUrl.endsWith('/ws')) {
if (wsUrl.endsWith('/')) {
wsUrl += 'ws';
} else {
wsUrl += '/ws';
}
}
// 检查是否有API密钥如果有则添加为查询参数
if (this.apiKey) {
const separator = wsUrl.includes('?') ? '&' : '?';
wsUrl += `${separator}apiKey=${encodeURIComponent(this.apiKey)}`;
}
// 检查环境和协议
// const isVsCodeWebview = typeof window !== 'undefined' && 'acquireVsCodeApi' in window;
const isLocalhost = wsUrl.includes('localhost') || wsUrl.includes('127.0.0.1');
// VS Code WebView环境下可能需要wss
// if (isVsCodeWebview && wsUrl.startsWith('ws://')) {
// this.logger('[WebSocketService] 在VS Code WebView中尝试使用安全WebSocket连接');
// wsUrl = wsUrl.replace('ws://', 'wss://');
// }
this.logger(`[WebSocketService] 最终WebSocket URL: ${wsUrl}`);
this.socket = new WebSocket(wsUrl);
this.setupEventHandlers();
// 处理连接失败的情况
if (isLocalhost) {
// 如果使用localhost失败尝试使用127.0.0.1
const fallbackTimeout = setTimeout(() => {
if (this.socket?.readyState !== WebSocket.OPEN) {
this.logger('[WebSocketService] localhost连接超时尝试使用127.0.0.1');
let fallbackUrl;
if (wsUrl.includes('localhost')) {
fallbackUrl = wsUrl.replace('localhost', '127.0.0.1');
} else if (wsUrl.includes('127.0.0.1')) {
fallbackUrl = wsUrl.replace('127.0.0.1', 'localhost');
}
if (fallbackUrl && fallbackUrl !== wsUrl) {
this.logger(`[WebSocketService] 尝试备用连接: ${fallbackUrl}`);
// 关闭旧连接
if (this.socket) {
this.socket.close();
}
// 尝试新连接
this.socket = new WebSocket(fallbackUrl);
this.setupEventHandlers();
}
}
clearTimeout(fallbackTimeout);
}, 3000); // 3秒后如果未连接则尝试备用地址
}
} catch (error) {
this.logger(`[WebSocketService] 连接错误: ${error instanceof Error ? error.message : String(error)}`);
this.emit(WebSocketEvent.ERROR, `连接错误: ${error instanceof Error ? error.message : String(error)}`);
this.scheduleReconnect();
}
}
/**
* 断开WebSocket连接
*/
public disconnect(): void {
this.clearTimers();
if (this.socket) {
try {
this.socket.close();
} catch (err) {
this.logger(`关闭WebSocket时出错: ${err}`);
}
this.socket = null;
}
this.isConnected = false;
this.emit(WebSocketEvent.DISCONNECTED);
}
/**
* 监听事件
*/
public on(event: WebSocketEvent, callback: (data?: any) => void): void {
if (!this.eventListeners.has(event)) {
this.eventListeners.set(event, []);
}
this.eventListeners.get(event)?.push(callback);
}
/**
* 移除事件监听
*/
public off(event: WebSocketEvent, callback: (data?: any) => void): void {
const listeners = this.eventListeners.get(event);
if (listeners) {
const index = listeners.indexOf(callback);
if (index !== -1) {
listeners.splice(index, 1);
}
}
}
/**
* 触发事件
*/
private emit(event: WebSocketEvent, data?: any): void {
const listeners = this.eventListeners.get(event);
if (listeners) {
listeners.forEach(callback => callback(data));
}
}
/**
* 设置WebSocket事件处理器
*/
private setupEventHandlers(): void {
if (!this.socket) {
this.logger('[WebSocketService] 无法设置事件处理器: socket为null');
return;
}
// 记录连接建立时间
let connectionEstablishedTime = 0;
// 使用浏览器风格的事件处理
this.socket.onopen = () => {
this.logger('[WebSocketService] WebSocket连接已建立成功');
this.isConnected = true;
connectionEstablishedTime = Date.now();
// 延迟重置重连计数器,确保连接稳定
setTimeout(() => {
if (this.isConnected) {
this.reconnectAttempts = 0;
this.logger('[WebSocketService] 连接保持稳定,重置重连计数器');
}
}, 5000);
// 设置定时ping保持连接
this.setupPingInterval();
this.emit(WebSocketEvent.CONNECTED);
this.logger('[WebSocketService] 已触发CONNECTED事件');
// 连接后立即获取模型列表
this.logger('[WebSocketService] 开始获取初始模型列表');
this.getModelList()
.then(models => {
this.logger(`[WebSocketService] 获取初始模型列表成功: ${JSON.stringify(models)}`);
this.emit(WebSocketEvent.MODEL_LIST, models);
})
.catch(error => {
this.logger(`[WebSocketService] 获取初始模型列表失败: ${error}`);
});
};
this.socket.onmessage = (event: MessageEvent) => {
try {
const data = event.data;
// 检查是否是HTTP错误响应
const stringData = typeof data === 'string' ? data : data.toString();
// 尝试解析JSON
let response;
try {
response = JSON.parse(stringData);
} catch (jsonError) {
this.logger(`无法解析JSON响应: ${jsonError}, 原始消息: ${stringData.substring(0, 100)}`);
this.emit(WebSocketEvent.ERROR, `服务器返回的不是有效JSON: ${stringData.substring(0, 50)}...`);
// 自动发送默认模型列表,避免前端卡住
this.emit(WebSocketEvent.MODEL_LIST, this.getDefaultModels());
return;
}
this.logger(`收到WebSocket消息: ${JSON.stringify(response)}`);
// 处理模型列表响应(特殊处理)
if (response.models && Array.isArray(response.models)) {
// 查找对应的模型列表请求
let modelRequestId = -1;
for (const [reqId, _] of this.requestMap.entries()) {
if (reqId < 100) { // 假设小ID是模型列表请求
modelRequestId = reqId;
break;
}
}
if (modelRequestId !== -1) {
const callback = this.requestMap.get(modelRequestId);
if (callback) {
callback(response);
this.requestMap.delete(modelRequestId);
}
}
// 无论找到对应请求与否,都通知模型列表更新
this.emit(WebSocketEvent.MODEL_LIST, response.models);
}
// 处理常规请求响应
else if (response.request_id && this.requestMap.has(response.request_id)) {
const callback = this.requestMap.get(response.request_id);
if (callback) {
callback(response);
// 如果不是流式响应或是最后一个包,清除请求
if (!response.stream_seq_id || response.stream_finsh) {
this.requestMap.delete(response.request_id);
}
}
}
} catch (error) {
this.logger(`解析WebSocket消息失败: ${error}, 原始消息: ${typeof event.data === 'string' ? event.data.substring(0, 100) : '非文本数据'}`);
// 自动发送默认模型列表,避免前端卡住
this.emit(WebSocketEvent.MODEL_LIST, this.getDefaultModels());
}
};
this.socket.onerror = (event: Event) => {
const errorDetails = JSON.stringify(event);
this.logger(`[WebSocketService] WebSocket错误事件: ${errorDetails}`);
// 检查是否包含isTrusted属性
if (event && 'isTrusted' in event && event.isTrusted) {
this.logger('[WebSocketService] 这是一个受信任的错误事件,可能是证书或安全设置导致的');
// 获取更多诊断信息
let diagInfo = '';
// 检查是否是localhost
if (this.apiUrl.includes('localhost') || this.apiUrl.includes('127.0.0.1')) {
diagInfo += '本地连接(localhost)可能需要特殊权限; ';
// 建议使用127.0.0.1而不是localhost
if (this.apiUrl.includes('localhost')) {
diagInfo += '建议尝试使用127.0.0.1替代localhost; ';
}
}
// 检查是否使用了wss安全连接
if (this.apiUrl.startsWith('wss://')) {
diagInfo += '使用了安全WebSocket连接请确认服务器证书有效; ';
} else {
// 如果使用ws不安全连接可能是混合内容问题
diagInfo += 'VS Code中使用不安全WebSocket(ws://)可能受到限制,建议使用安全连接(wss://); ';
}
// 检查WebView上下文
if (typeof window !== 'undefined' && 'acquireVsCodeApi' in window) {
diagInfo += 'WebView环境中可能有额外的安全限制; ';
}
const errorMessage = `连接错误(isTrusted): ${diagInfo}请检查服务器配置和网络连接`;
this.emit(WebSocketEvent.ERROR, errorMessage);
} else {
this.emit(WebSocketEvent.ERROR, `连接错误: ${errorDetails}`);
}
// 重连
if (this.socket?.readyState !== WebSocket.OPEN) {
this.logger('[WebSocketService] Socket未处于OPEN状态安排重连');
this.scheduleReconnect();
}
};
this.socket.onclose = (event: CloseEvent) => {
this.logger(`[WebSocketService] WebSocket连接关闭: 代码=${event.code} (${this.getCloseEventReason(event.code)}), 原因=${event.reason || '未提供'}`);
this.isConnected = false;
// 计算连接持续时间
if (connectionEstablishedTime > 0) {
const duration = Date.now() - connectionEstablishedTime;
this.logger(`[WebSocketService] 连接持续时间: ${duration}ms`);
}
this.clearTimers();
this.emit(WebSocketEvent.DISCONNECTED);
this.logger('[WebSocketService] 已触发DISCONNECTED事件');
// 如果不是正常关闭,尝试重连
if (event.code !== 1000) {
this.logger('[WebSocketService] 非正常关闭,安排重连');
this.scheduleReconnect();
}
};
this.logger('[WebSocketService] 所有WebSocket事件处理器已设置完成');
}
/**
* 获取模型列表
*/
public async getModelList(): Promise<string[]> {
try {
const requestId = this.nextRequestId++;
// 按照API文档使用list_model命令
const request = {
request_id: requestId,
cmd: 'list_model' // 使用文档中指定的命令
};
this.logger(`请求模型列表 (ID: ${requestId})`);
return new Promise<string[]>((resolve) => {
// 设置5秒超时超时后返回默认模型
const timeout = setTimeout(() => {
if (this.requestMap.has(requestId)) {
this.logger(`模型列表请求超时 (ID: ${requestId})`);
this.requestMap.delete(requestId);
resolve(this.getDefaultModels());
}
}, 5000);
// 设置响应处理器
this.requestMap.set(requestId, (response) => {
clearTimeout(timeout);
if (response.error) {
this.logger(`获取模型列表错误: ${response.error}`);
resolve(this.getDefaultModels());
} else {
// 根据文档应该使用models字段
const models = response.models || [];
this.logger(`获取到模型列表: ${JSON.stringify(models)}`);
if (models.length === 0) {
resolve(this.getDefaultModels());
} else {
resolve(models);
}
}
});
// 发送请求
this.sendRequest(request);
});
} catch (error) {
this.logger(`获取模型列表异常: ${error}`);
return this.getDefaultModels();
}
}
/**
* 发送聊天消息
*/
public async sendChatMessage(message: string, model: string): Promise<string> {
if (!this.isConnected) {
this.logger('无法发送消息: WebSocket未连接');
throw new Error('WebSocket未连接');
}
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
this.logger(`WebSocket状态异常: ${this.socket ? this.getReadyStateDescription(this.socket.readyState) : 'socket为null'}`);
throw new Error(`WebSocket未准备好: ${this.socket ? this.getReadyStateDescription(this.socket.readyState) : 'socket为null'}`);
}
const requestId = this.nextRequestId++;
// 按照API文档中的格式
const request = {
cmd: 'exec_chat', // 使用exec_chat命令
request_id: requestId,
msg: message, // 使用msg字段而不是content
model: model,
stream: true
};
this.logger(`发送聊天消息 (ID: ${requestId}): ${message.substring(0, 50)}${message.length > 50 ? '...' : ''}`);
this.logger(`使用模型: ${model}`);
// 创建用户消息
const userMessage: ChatMessage = {
id: `request-${requestId}`,
content: message,
role: 'user',
createAt: new Date()
};
// 通知消息已创建
this.emit(WebSocketEvent.MESSAGE, userMessage);
return new Promise<string>((resolve, reject) => {
let responseContent = '';
let responseStartTime: number | null = null;
let lastResponseTime: number | null = null;
// 设置超时处理
const timeoutId = setTimeout(() => {
this.logger(`聊天消息响应超时 (ID: ${requestId})`);
this.requestMap.delete(requestId);
reject(new Error('服务器响应超时,请稍后重试'));
}, 60000); // 60秒超时
this.requestMap.set(requestId, (response) => {
// 记录第一次收到响应的时间
if (responseStartTime === null) {
responseStartTime = Date.now();
this.logger(`收到第一个响应包 (ID: ${requestId}), 延迟: ${Date.now() - lastResponseTime!}ms`);
}
// 更新最后响应时间
lastResponseTime = Date.now();
if (response.error) {
clearTimeout(timeoutId);
this.logger(`聊天消息响应错误 (ID: ${requestId}): ${response.error}`);
reject(new Error(response.error));
return;
}
// 处理消息内容 - 适配不同的响应字段名
if (response.msg) {
responseContent += response.msg;
// 创建助手消息
const assistantMessage: ChatMessage = {
id: `response-${requestId}-${response.stream_seq_id || 0}`,
content: responseContent,
role: 'assistant',
createAt: new Date()
};
// 通知有新消息
this.emit(WebSocketEvent.MESSAGE, assistantMessage);
}
// 检查是否是最后一个响应包
if (!response.stream_seq_id || response.stream_finsh) {
clearTimeout(timeoutId);
const totalTime = Date.now() - responseStartTime!;
this.logger(`聊天消息完成 (ID: ${requestId}), 总耗时: ${totalTime}ms`);
resolve(responseContent);
}
});
// 记录发送时间
lastResponseTime = Date.now();
try {
this.sendRequest(request);
} catch (error) {
clearTimeout(timeoutId);
this.logger(`发送请求失败 (ID: ${requestId}): ${error}`);
this.requestMap.delete(requestId);
reject(error);
}
});
}
/**
* 获取WebSocket ReadyState的描述
*/
private getReadyStateDescription(state: number): string {
switch (state) {
case WebSocket.CONNECTING:
return '正在连接 (CONNECTING: 0)';
case WebSocket.OPEN:
return '已连接 (OPEN: 1)';
case WebSocket.CLOSING:
return '正在关闭 (CLOSING: 2)';
case WebSocket.CLOSED:
return '已关闭 (CLOSED: 3)';
default:
return `未知状态: ${state}`;
}
}
/**
* 设置心跳检测
*/
private setupPingInterval(): void {
this.clearTimers();
// 每30秒发送一次空消息以保持连接
this.pingInterval = window.setInterval(() => {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
try {
// 发送空心跳消息
this.socket.send(JSON.stringify({ type: 'ping' }));
} catch (error) {
this.logger(`发送心跳包失败: ${error}`);
}
}
}, 30000);
}
/**
* 安排重连
*/
private scheduleReconnect(): void {
if (this.isReconnecting || this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
this.logger(`达到最大重连尝试次数 (${this.MAX_RECONNECT_ATTEMPTS}),停止重连`);
this.emit(WebSocketEvent.ERROR, `连接失败:已尝试 ${this.MAX_RECONNECT_ATTEMPTS} 次重连,请检查网络或服务器状态后手动重连`);
}
return;
}
this.isReconnecting = true;
this.reconnectAttempts++;
// 使用指数退避策略和随机抖动
const baseDelay = this.RECONNECT_DELAY * Math.pow(1.5, this.reconnectAttempts - 1);
const jitter = Math.random() * 1000; // 添加最多1秒的随机抖动
const delay = Math.min(baseDelay + jitter, 30000); // 上限为30秒
this.logger(`尝试重新连接 (${this.reconnectAttempts}/${this.MAX_RECONNECT_ATTEMPTS}), 延迟 ${delay}ms`);
this.reconnectTimer = window.setTimeout(() => {
this.isReconnecting = false;
this.connect();
}, delay);
}
/**
* 清除所有定时器
*/
private clearTimers(): void {
if (this.pingInterval !== null) {
window.clearInterval(this.pingInterval);
this.pingInterval = null;
}
if (this.reconnectTimer !== null) {
window.clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
/**
* 发送WebSocket请求
*/
private sendRequest(request: any): void {
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
this.logger('无法发送请求: WebSocket未连接');
throw new Error('WebSocket未连接');
}
try {
const requestStr = JSON.stringify(request);
this.socket.send(requestStr);
} catch (error) {
this.logger(`发送WebSocket请求失败: ${error}`);
throw error;
}
}
/**
* 获取默认模型列表
*/
private getDefaultModels(): string[] {
return ['qwq:latest', 'deepseek-r1:32b', 'qwen2.5-coder:7b'];
}
/**
* 获取WebSocket关闭代码解释
*/
private getCloseEventReason(code: number): string {
const explanations: Record<number, string> = {
1000: '正常关闭',
1001: '端点离开',
1002: '协议错误',
1003: '无法接受数据',
1004: '保留',
1005: '未提供状态码',
1006: '异常关闭',
1007: '数据类型不一致',
1008: '违反策略',
1009: '消息太大',
1010: '必需的扩展缺失',
1011: '内部错误',
1012: '服务重启',
1013: '临时错误',
1014: '服务器超载',
1015: 'TLS握手失败'
};
return explanations[code] || `未知代码: ${code}`;
}
}
// 创建聊天状态管理
export const useChatStore = defineStore('chat', () => {
// WebSocket服务实例
const wsService = ref<WebSocketService | null>(null);
// 聊天状态
const chatState = reactive({
messages: [] as ChatMessage[],
loading: false,
connectionStatus: 'disconnected' as 'connecting' | 'connected' | 'disconnected' | 'error',
errorMessage: '',
availableModels: [] as string[],
currentModel: '',
loadingModels: false
});
// 初始化WebSocket服务
function initWebSocketService(apiUrl: string, apiKey: string) {
// 记录函数调用到VSCode日志
const logToVSCode = (message: string) => {
try {
if (window.vscodeApi) {
window.vscodeApi.postMessage({
type: 'log',
message: `[WebSocketInit] ${message}`
});
}
} catch (error) {
console.error('向VSCode发送日志失败:', error);
}
};
logToVSCode(`初始化WebSocket服务开始: ${apiUrl}`);
console.log(`[ChatStore] 初始化WebSocket服务开始: ${apiUrl}`);
try {
if (!apiUrl || !apiKey) {
const errorMsg = 'API URL或API Key为空无法初始化WebSocket服务';
console.error('[ChatStore] ' + errorMsg);
logToVSCode(errorMsg);
chatState.errorMessage = 'API配置不完整请先完成配置';
chatState.connectionStatus = 'error';
return;
}
// 如果已存在WebSocket服务先断开连接
if (wsService.value) {
console.log('[ChatStore] 断开现有WebSocket连接');
logToVSCode('断开现有WebSocket连接');
wsService.value.disconnect();
}
// 创建新的WebSocket服务实例
console.log('[ChatStore] 创建新的WebSocket服务实例');
logToVSCode('创建新的WebSocket服务实例');
wsService.value = new WebSocketService(apiUrl, apiKey, (message) => {
// 确保日志同时发送到控制台和VSCode
console.log(`[WebSocketService] ${message}`);
if (window.vscodeApi) {
window.vscodeApi.postMessage({
type: 'log',
message: `[WebSocketService] ${message}`
});
}
});
// 设置事件监听
console.log('[ChatStore] 设置WebSocket事件监听');
logToVSCode('设置WebSocket事件监听');
setupWebSocketEvents();
// 连接WebSocket
console.log('[ChatStore] 开始连接WebSocket');
logToVSCode('开始连接WebSocket');
chatState.connectionStatus = 'connecting';
wsService.value.connect();
// 5秒后检查连接状态
setTimeout(() => {
if (!wsService.value) {
console.error('[ChatStore] WebSocket服务实例为null');
logToVSCode('WebSocket服务实例为null');
return;
}
if (!wsService.value.isConnected) {
console.error('[ChatStore] WebSocket连接超时');
logToVSCode('WebSocket连接超时');
chatState.errorMessage = '连接超时请检查API配置是否正确';
chatState.connectionStatus = 'error';
} else {
console.log('[ChatStore] WebSocket连接成功');
logToVSCode('WebSocket连接成功');
}
}, 5000);
console.log('[ChatStore] 初始化WebSocket服务完成');
logToVSCode('初始化WebSocket服务完成');
} catch (error) {
console.error('[ChatStore] 初始化WebSocket服务异常:', error);
logToVSCode(`初始化WebSocket服务异常: ${error instanceof Error ? error.message : String(error)}`);
chatState.errorMessage = `初始化WebSocket失败: ${error instanceof Error ? error.message : String(error)}`;
chatState.connectionStatus = 'error';
}
}
// 设置WebSocket事件监听
function setupWebSocketEvents() {
if (!wsService.value) {
console.error('[ChatStore] setupWebSocketEvents失败: wsService为null');
return;
}
console.log('[ChatStore] 开始设置WebSocket事件监听...');
wsService.value.on(WebSocketEvent.CONNECTED, () => {
console.log('[ChatStore] 收到WebSocket CONNECTED事件');
chatState.connectionStatus = 'connected';
chatState.errorMessage = '';
});
wsService.value.on(WebSocketEvent.DISCONNECTED, () => {
console.log('[ChatStore] 收到WebSocket DISCONNECTED事件');
chatState.connectionStatus = 'disconnected';
});
wsService.value.on(WebSocketEvent.ERROR, (error: string) => {
console.error('[ChatStore] 收到WebSocket ERROR事件:', error);
chatState.errorMessage = error;
chatState.connectionStatus = 'error';
chatState.loading = false;
chatState.loadingModels = false;
});
wsService.value.on(WebSocketEvent.MESSAGE, (message: ChatMessage) => {
console.log('[ChatStore] 收到WebSocket MESSAGE事件');
// 查找是否已存在具有相同ID的消息
const existingIndex = chatState.messages.findIndex(m => m.id === message.id);
if (existingIndex >= 0) {
// 更新现有消息
chatState.messages[existingIndex] = message;
} else {
// 添加新消息
chatState.messages.push(message);
}
chatState.loading = false;
});
wsService.value.on(WebSocketEvent.MODEL_LIST, (models: string[]) => {
console.log('[ChatStore] 收到WebSocket MODEL_LIST事件:', models);
chatState.loadingModels = false;
if (Array.isArray(models) && models.length > 0) {
chatState.availableModels = models;
// 如果没有当前模型,设置第一个为当前模型
if (!chatState.currentModel && models.length > 0) {
chatState.currentModel = models[0];
}
}
});
console.log('[ChatStore] WebSocket事件监听器设置完成');
}
// 发送消息
async function sendMessage(content: string) {
console.log('Store: 开始发送消息', { content, connectionStatus: chatState.connectionStatus });
if (!wsService.value || !wsService.value.isConnected) {
console.error('Store: WebSocket未连接无法发送消息');
chatState.errorMessage = '未连接到AI服务请先连接';
chatState.connectionStatus = 'error';
throw new Error('WebSocket未连接');
}
if (!content.trim() || chatState.loading) {
console.log('Store: 消息为空或正在加载中,不发送');
return;
}
// 确保有当前模型
if (!chatState.currentModel && chatState.availableModels.length > 0) {
console.log('Store: 自动选择模型', chatState.availableModels[0]);
chatState.currentModel = chatState.availableModels[0];
} else if (!chatState.currentModel) {
console.error('Store: 没有可用模型');
throw new Error('没有可用的AI模型');
}
console.log(`Store: 准备发送消息到WebSocket使用模型: ${chatState.currentModel}`);
chatState.loading = true;
try {
// 记录开始时间,用于计算响应时间
const startTime = Date.now();
// 直接发送消息到WebSocket服务
const response = await wsService.value.sendChatMessage(content, chatState.currentModel);
// 计算响应时间
const responseTime = Date.now() - startTime;
console.log(`Store: 收到WebSocket响应耗时: ${responseTime}ms`);
return response;
} catch (error) {
console.error('Store: 发送消息失败:', error);
chatState.errorMessage = `发送消息失败: ${error instanceof Error ? error.message : '未知错误'}`;
chatState.connectionStatus = 'error';
chatState.loading = false;
throw error;
}
}
// 刷新模型列表
async function refreshModels() {
if (!wsService.value || !wsService.value.isConnected) {
return;
}
chatState.loadingModels = true;
try {
const models = await wsService.value.getModelList();
if (Array.isArray(models) && models.length > 0) {
chatState.availableModels = models;
}
chatState.loadingModels = false;
} catch (error) {
chatState.loadingModels = false;
}
}
// 设置当前模型
function setCurrentModel(model: string) {
chatState.currentModel = model;
}
// 清空消息
function clearMessages() {
chatState.messages = [];
}
// 手动重连
function reconnect() {
if (wsService.value) {
wsService.value.disconnect();
chatState.connectionStatus = 'connecting';
wsService.value.connect();
}
}
return {
chatState,
initWebSocketService,
sendMessage,
refreshModels,
setCurrentModel,
clearMessages,
reconnect
};
});