/** * AI服务类 * 处理与AI模型的通信和响应 */ class AIService { constructor() { this.apiEndpoint = CONFIG.AI_API.ENDPOINT; this.model = CONFIG.AI_API.MODEL; this.context = []; this.currentExcelData = null; // 保存当前Excel数据 // 使用默认API密钥 this.apiKey = CONFIG.AI_API.DEFAULT_API_KEY; this.controller = null; // 用于中断请求的 AbortController this.currentPageInfo = null; // 保存当前页面信息 } /** * 初始化AI服务 */ async init() { try { // 尝试从storage中获取用户设置的API密钥 const result = await Utils.getStorageData("apiKey"); if (result?.apiKey) { this.apiKey = result.apiKey; } console.log("AI Service initialized"); } catch (error) { console.error("Failed to initialize AI service:", error); } } /** * 设置API密钥 * @param {string} apiKey */ async setApiKey(apiKey) { this.apiKey = apiKey; await Utils.setStorageData("apiKey", { apiKey }); } /** * 发送消息到DeepSeek API * @param {string} message 用户消息 * @returns {Promise} AI响应 */ async sendMessage(message) { try { // 创建新的 AbortController this.controller = new AbortController(); const response = await fetch(this.apiEndpoint, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ model: this.model, messages: this.formatMessages(message), max_tokens: CONFIG.AI_API.MAX_TOKENS, temperature: CONFIG.AI_API.TEMPERATURE, }), signal: this.controller.signal, }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } const data = await response.json(); const aiResponse = data.choices[0]?.message?.content; if (!aiResponse) { throw new Error("无效的API响应"); } return aiResponse; } catch (error) { if (error.name === "AbortError") { throw new Error("REQUEST_ABORTED"); } console.error("API call failed:", error); throw error; } finally { this.controller = null; } } /** * 格式化消息历史 * @param {string} currentMessage 当前消息 * @returns {Array} 格式化后的消息数组 */ formatMessages(currentMessage) { const messages = this.context.map((msg) => ({ role: msg.role, content: msg.content, })); // 如果存在页面信息,添加到当前消息的上下文 if (this.currentPageInfo) { currentMessage = `基于之前总结的页面内容(标题:${this.currentPageInfo.title}),${currentMessage}`; } messages.push({ role: "user", content: currentMessage, }); return messages; } /** * 更新对话上下文 * @param {string} message 新消息 * @param {string} role 消息角色(user/assistant) */ updateContext(message, role) { this.context.push({ role, content: message, timestamp: new Date().toISOString(), }); // 保持上下文长度在合理范围内 if (this.context.length > 10) { this.context = this.context.slice(-10); } } /** * 清除对话上下文 */ clearContext() { this.context = []; } /** * 获取当前对话上下文 * @returns {Array} 对话上下文数组 */ getContext() { return this.context; } // 添加中断方法 abortRequest() { if (this.controller) { this.controller.abort(); this.controller = null; } } /** * 获取页面总结提示词 * @param {Object} pageInfo 页面信息 * @returns {string} 提示词 */ getSummaryPrompt(pageInfo) { return `请帮我总结以下网页内容的要点: 页面标题:${pageInfo.title} 网站:${pageInfo.siteName} URL:${pageInfo.url} 主要内容: ${pageInfo.mainContent} 要求: 1. 提取3-5个核心要点 2. 使用简洁清晰的语言 3. 保持客观中立的语气 4. 按重要性排序 5. 如果内容是新闻,需要包含时间、地点、人物等关键信息 6. 如果内容是教程,需要突出操作步骤和关键提示 7. 如果内容是产品介绍,需要包含主要特点和优势 请以"以下是对该页面内容的总结:"开头,然后用要点的形式列出主要内容。`; } /** * 设置当前Excel数据 * @param {Object} data Excel数据和元信息 */ setExcelData(data) { this.currentExcelData = data; } /** * 设置当前页面信息 * @param {Object} pageInfo 页面信息 */ setPageInfo(pageInfo) { this.currentPageInfo = pageInfo; } } // 确保在DOM加载完成后再创建实例 document.addEventListener("DOMContentLoaded", () => { // 只有在实例不存在时才创建 if (!window.aiService) { window.aiService = new AIService(); } });