ai-service.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * AI服务类
  3. * 处理与AI模型的通信和响应
  4. */
  5. class AIService {
  6. constructor() {
  7. this.apiEndpoint = CONFIG.AI_API.ENDPOINT;
  8. this.model = CONFIG.AI_API.MODEL;
  9. this.context = [];
  10. this.currentExcelData = null; // 保存当前Excel数据
  11. // 使用默认API密钥
  12. this.apiKey = CONFIG.AI_API.DEFAULT_API_KEY;
  13. this.controller = null; // 用于中断请求的 AbortController
  14. this.currentPageInfo = null; // 保存当前页面信息
  15. }
  16. /**
  17. * 初始化AI服务
  18. */
  19. async init() {
  20. try {
  21. // 尝试从storage中获取用户设置的API密钥
  22. const result = await Utils.getStorageData("apiKey");
  23. if (result?.apiKey) {
  24. this.apiKey = result.apiKey;
  25. }
  26. console.log("AI Service initialized");
  27. } catch (error) {
  28. console.error("Failed to initialize AI service:", error);
  29. }
  30. }
  31. /**
  32. * 设置API密钥
  33. * @param {string} apiKey
  34. */
  35. async setApiKey(apiKey) {
  36. this.apiKey = apiKey;
  37. await Utils.setStorageData("apiKey", { apiKey });
  38. }
  39. /**
  40. * 发送消息到DeepSeek API
  41. * @param {string} message 用户消息
  42. * @returns {Promise<string>} AI响应
  43. */
  44. async sendMessage(message) {
  45. try {
  46. // 创建新的 AbortController
  47. this.controller = new AbortController();
  48. const response = await fetch(this.apiEndpoint, {
  49. method: "POST",
  50. headers: {
  51. "Content-Type": "application/json",
  52. Authorization: `Bearer ${this.apiKey}`,
  53. },
  54. body: JSON.stringify({
  55. model: this.model,
  56. messages: this.formatMessages(message),
  57. max_tokens: CONFIG.AI_API.MAX_TOKENS,
  58. temperature: CONFIG.AI_API.TEMPERATURE,
  59. }),
  60. signal: this.controller.signal,
  61. });
  62. if (!response.ok) {
  63. throw new Error(`API request failed: ${response.status}`);
  64. }
  65. const data = await response.json();
  66. const aiResponse = data.choices[0]?.message?.content;
  67. if (!aiResponse) {
  68. throw new Error("无效的API响应");
  69. }
  70. return aiResponse;
  71. } catch (error) {
  72. if (error.name === "AbortError") {
  73. throw new Error("REQUEST_ABORTED");
  74. }
  75. console.error("API call failed:", error);
  76. throw error;
  77. } finally {
  78. this.controller = null;
  79. }
  80. }
  81. /**
  82. * 格式化消息历史
  83. * @param {string} currentMessage 当前消息
  84. * @returns {Array} 格式化后的消息数组
  85. */
  86. formatMessages(currentMessage) {
  87. const messages = this.context.map((msg) => ({
  88. role: msg.role,
  89. content: msg.content,
  90. }));
  91. // 如果存在页面信息,添加到当前消息的上下文
  92. if (this.currentPageInfo) {
  93. currentMessage = `基于之前总结的页面内容(标题:${this.currentPageInfo.title}),${currentMessage}`;
  94. }
  95. messages.push({
  96. role: "user",
  97. content: currentMessage,
  98. });
  99. return messages;
  100. }
  101. /**
  102. * 更新对话上下文
  103. * @param {string} message 新消息
  104. * @param {string} role 消息角色(user/assistant)
  105. */
  106. updateContext(message, role) {
  107. this.context.push({
  108. role,
  109. content: message,
  110. timestamp: new Date().toISOString(),
  111. });
  112. // 保持上下文长度在合理范围内
  113. if (this.context.length > 10) {
  114. this.context = this.context.slice(-10);
  115. }
  116. }
  117. /**
  118. * 清除对话上下文
  119. */
  120. clearContext() {
  121. this.context = [];
  122. }
  123. /**
  124. * 获取当前对话上下文
  125. * @returns {Array} 对话上下文数组
  126. */
  127. getContext() {
  128. return this.context;
  129. }
  130. // 添加中断方法
  131. abortRequest() {
  132. if (this.controller) {
  133. this.controller.abort();
  134. this.controller = null;
  135. }
  136. }
  137. /**
  138. * 获取页面总结提示词
  139. * @param {Object} pageInfo 页面信息
  140. * @returns {string} 提示词
  141. */
  142. // 4. 按重要性排序
  143. // 5. 如果内容是新闻,需要包含时间、地点、人物等关键信息
  144. // 6. 如果内容是教程,需要突出操作步骤和关键提示
  145. // 7. 如果内容是产品介绍,需要包含主要特点和优势
  146. getSummaryPrompt(pageInfo) {
  147. return `请帮我总结以下网页内容的要点:
  148. 页面标题:${pageInfo.title}
  149. 网站:${pageInfo.siteName}
  150. URL:${pageInfo.url}
  151. 主要内容:
  152. ${pageInfo.mainContent}
  153. 要求:
  154. 1. 帮助我分析表单项与上传excel文件中每一列的关系
  155. 2- 将excel文件中每一列的内容与表单项进行匹配,并生成对应的数组,在一个字段内返回
  156. `;
  157. }
  158. /**
  159. * 设置当前Excel数据
  160. * @param {Object} data Excel数据和元信息
  161. */
  162. setExcelData(data) {
  163. this.currentExcelData = data;
  164. }
  165. /**
  166. * 设置当前页面信息
  167. * @param {Object} pageInfo 页面信息
  168. */
  169. setPageInfo(pageInfo) {
  170. this.currentPageInfo = pageInfo;
  171. }
  172. }
  173. // 确保在DOM加载完成后再创建实例
  174. document.addEventListener("DOMContentLoaded", () => {
  175. // 只有在实例不存在时才创建
  176. if (!window.aiService) {
  177. window.aiService = new AIService();
  178. }
  179. });