ai-service.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. // 使用默认API密钥
  11. this.apiKey = CONFIG.AI_API.DEFAULT_API_KEY;
  12. this.controller = null; // 用于中断请求的 AbortController
  13. }
  14. /**
  15. * 初始化AI服务
  16. */
  17. async init() {
  18. try {
  19. // 尝试从storage中获取用户设置的API密钥
  20. const result = await Utils.getStorageData("apiKey");
  21. if (result?.apiKey) {
  22. this.apiKey = result.apiKey;
  23. }
  24. console.log("AI Service initialized");
  25. } catch (error) {
  26. console.error("Failed to initialize AI service:", error);
  27. }
  28. }
  29. /**
  30. * 设置API密钥
  31. * @param {string} apiKey
  32. */
  33. async setApiKey(apiKey) {
  34. this.apiKey = apiKey;
  35. await Utils.setStorageData("apiKey", { apiKey });
  36. }
  37. /**
  38. * 发送消息到DeepSeek API
  39. * @param {string} message 用户消息
  40. * @returns {Promise<string>} AI响应
  41. */
  42. async sendMessage(message) {
  43. try {
  44. // 创建新的 AbortController
  45. this.controller = new AbortController();
  46. const response = await fetch(this.apiEndpoint, {
  47. method: "POST",
  48. headers: {
  49. "Content-Type": "application/json",
  50. Authorization: `Bearer ${this.apiKey}`,
  51. },
  52. body: JSON.stringify({
  53. model: this.model,
  54. messages: this.formatMessages(message),
  55. max_tokens: CONFIG.AI_API.MAX_TOKENS,
  56. temperature: CONFIG.AI_API.TEMPERATURE,
  57. }),
  58. signal: this.controller.signal,
  59. });
  60. if (!response.ok) {
  61. throw new Error(`API request failed: ${response.status}`);
  62. }
  63. const data = await response.json();
  64. const aiResponse = data.choices[0]?.message?.content;
  65. if (!aiResponse) {
  66. throw new Error("无效的API响应");
  67. }
  68. return aiResponse;
  69. } catch (error) {
  70. if (error.name === "AbortError") {
  71. throw new Error("REQUEST_ABORTED");
  72. }
  73. console.error("API call failed:", error);
  74. throw error;
  75. } finally {
  76. this.controller = null;
  77. }
  78. }
  79. /**
  80. * 格式化消息历史
  81. * @param {string} currentMessage 当前消息
  82. * @returns {Array} 格式化后的消息数组
  83. */
  84. formatMessages(currentMessage) {
  85. const messages = this.context.map((msg) => ({
  86. role: msg.role,
  87. content: msg.content,
  88. }));
  89. messages.push({
  90. role: "user",
  91. content: currentMessage,
  92. });
  93. return messages;
  94. }
  95. /**
  96. * 更新对话上下文
  97. * @param {string} message 新消息
  98. * @param {string} role 消息角色(user/assistant)
  99. */
  100. updateContext(message, role) {
  101. this.context.push({
  102. role,
  103. content: message,
  104. timestamp: new Date().toISOString(),
  105. });
  106. // 保持上下文长度在合理范围内
  107. if (this.context.length > 10) {
  108. this.context = this.context.slice(-10);
  109. }
  110. }
  111. /**
  112. * 清除对话上下文
  113. */
  114. clearContext() {
  115. this.context = [];
  116. }
  117. /**
  118. * 获取当前对话上下文
  119. * @returns {Array} 对话上下文数组
  120. */
  121. getContext() {
  122. return this.context;
  123. }
  124. // 添加中断方法
  125. abortRequest() {
  126. if (this.controller) {
  127. this.controller.abort();
  128. this.controller = null;
  129. }
  130. }
  131. }
  132. // 确保在DOM加载完成后再创建实例
  133. document.addEventListener("DOMContentLoaded", () => {
  134. // 只有在实例不存在时才创建
  135. if (!window.aiService) {
  136. window.aiService = new AIService();
  137. }
  138. });