/** * AI服务类 * 处理与AI模型的通信和响应 */ class AIService { constructor() { this.apiEndpoint = CONFIG.AI_API.ENDPOINT; this.messageContainer = document.getElementById("chat-messages"); this.model = CONFIG.AI_API.MODEL; this.context = []; this.currentExcelData = null; // 保存当前Excel数据 this.response = '' // 使用默认API密钥 this.apiKey = CONFIG.AI_API.DEFAULT_API_KEY; this.controller = null; // 用于中断请求的 AbortController this.currentPageInfo = null; // 保存当前页面信息 this.openai = new OpenAI( { // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx", apiKey: 'sk-e9855234f47346049809ce23ed3ebe3f', baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", dangerouslyAllowBrowser: true, } ); } /** * 初始化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 a = +new Date() const response = await this.openai.chat.completions.create({ model: "qwen-plus", //模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models messages: this.formatMessages(message), stream:true }); // console.log(completion); const signal = response.controller.signal; let text = '匹配到的关系为:' const paragraph = window.chatUI.addMessage(text, "assistant"); try { const iterator = response.iterator(); for await (const chunk of iterator) { if (chunk) { console.log(chunk); const decodedChunk = chunk.choices[0].delta.content; if (decodedChunk) { text += decodedChunk; paragraph.innerHTML = text } } } console.log(document.getElementById("fill-button")); document.getElementById("fill-button").onclick = () => { window.parent.postMessage({ type: "HANDLE_FILL_INPUT", data: { formData: JSON.parse(this.response.split('json')[1].split('```')[0]) //根据不同返回修改res } }, "*"); } this.response = text console.log(text); } catch (error) { if (signal.aborted) { console.log("Stream reading aborted"); } else { console.error("Error reading stream:", error); } } console.log((+new Date() - a) / 1000) // console.log(completion.choices[0].message.content); // 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), // }), // 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; // const aiResponse = completion.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} 提示词 */ // 4. 按重要性排序 // 5. 如果内容是新闻,需要包含时间、地点、人物等关键信息 // 6. 如果内容是教程,需要突出操作步骤和关键提示 // 7. 如果内容是产品介绍,需要包含主要特点和优势 getSummaryPrompt(pageInfo) { return `请帮我总结以下网页内容的要点: 页面标题:${pageInfo.title} 网站:${pageInfo.siteName} URL:${pageInfo.url} 主要内容: ${pageInfo.mainContent} 要求: 1. 帮助我分析表单项与上传excel文件中每一列的关系 2- 将excel文件中每一列的内容与表单项进行匹配,并生成对应的数组,在一个字段内返回 `; } /** * 设置当前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(); } });