ai-service.js 4.5 KB

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