ai-service.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import OpenAI from 'openai'
  2. import hljs from 'highlight.js'
  3. import 'highlight.js/styles/atom-one-dark.css' // 导入一个暗色主题样式
  4. export const openai = new OpenAI({
  5. // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",
  6. apiKey: 'sk-e9855234f47346049809ce23ed3ebe3f',
  7. baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
  8. dangerouslyAllowBrowser: true
  9. })
  10. export async function getFileContent(data) {
  11. try {
  12. const res = await fetch('http://180.76.147.97:18899/upload_file', {
  13. method: 'post',
  14. body: data
  15. }).then((res) => res.json())
  16. return res
  17. } catch (error) {}
  18. }
  19. export async function getFormKey(data) {
  20. try {
  21. const res = await fetch('http://180.76.147.97:18899/uie', {
  22. method: 'post',
  23. body: JSON.stringify(data)
  24. }).then((res) => res.json())
  25. return res
  26. } catch (error) {}
  27. }
  28. export async function hepl(data) {
  29. try {
  30. const res = await fetch(' http://180.76.147.97:18899/test', {
  31. method: 'post',
  32. body: JSON.stringify(data)
  33. }).then((res) => res.json())
  34. return res
  35. } catch (error) {}
  36. }
  37. export async function sendMessage(message) {
  38. try {
  39. // 创建新的 AbortController
  40. const controller = new AbortController()
  41. const a = +new Date()
  42. const response = await openai.chat.completions.create({
  43. model: 'qwen-plus', //模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
  44. messages: message,
  45. stream: true
  46. })
  47. return response
  48. } catch (error) {
  49. console.error('API call failed:', error)
  50. return '[FILE]'
  51. } finally {
  52. }
  53. }
  54. function formatMessages(currentMessage, Summary, html) {
  55. const messages = [
  56. {
  57. role: 'user',
  58. content: currentMessage
  59. }
  60. ]
  61. // const messages = this.context.filter(msg => msg.role === 'user').map((msg) => ({
  62. // role: msg.role,
  63. // content: msg.content,
  64. // }));
  65. // console.log(currentMessage, this.currentPageInfo, input);
  66. // // 如果存在页面信息,添加到当前消息的上下文
  67. // if (this.currentPageInfo && !input) {
  68. // currentMessage = `基于之前总结的页面内容(标题:${JSON.stringify(this.currentPageInfo)}),${currentMessage}`;
  69. // }
  70. // messages.push()
  71. if (Summary) return messages
  72. if (html)
  73. messages.unshift({
  74. role: 'user',
  75. content: `页面主要内容${html}`
  76. })
  77. return messages
  78. }
  79. export function getSummaryPrompt(pageInfo) {
  80. return `请帮我总结以下网页内容的要点:
  81. 页面标题:${pageInfo.title}
  82. 网站:${pageInfo.siteName}
  83. URL:${pageInfo.url}
  84. 主要内容:
  85. ${pageInfo.mainContent}
  86. 要求:
  87. 1. 用简洁清晰的语言提取3-5个核心要点
  88. 2. 保持客观中立的语气
  89. 3. 按重要性排序
  90. 4. 如果内容是新闻,需要包含时间、地点、人物等关键信息
  91. 5. 如果内容是教程,需要突出操作步骤和关键提示
  92. 6. 如果内容是产品介绍,需要包含主要特点和优势
  93. 7. 返回内容做好换行,以及展示样式
  94. 请以"以下是对该页面内容的总结:"开头,然后用要点的形式列出主要内容。`
  95. }
  96. export function getFileSummaryPrompt(file, name) {
  97. return `请帮我总结以下文件内容的要点:
  98. 文件名称:${name}
  99. 主要内容:
  100. ${file}
  101. 要求:
  102. 1. 用简洁清晰的语言提取3-5个核心要点
  103. 2. 保持客观中立的语气
  104. 3. 按重要性排序
  105. 4. 返回内容做好换行,以及展示样式
  106. 请以"以下是对该文件内容的总结:"开头,然后用要点的形式列出主要内容。`
  107. }
  108. export function formatMessage(text) {
  109. if (!text) return ''
  110. // 用于转义代码块中的 HTML 字符
  111. const escapeHtml = (str) => {
  112. return str
  113. .replace(/&/g, '&')
  114. .replace(/</g, '&lt;')
  115. .replace(/>/g, '&gt;')
  116. .replace(/"/g, '&quot;')
  117. .replace(/'/g, '&#039;')
  118. }
  119. // 处理代码高亮
  120. const highlightCode = (code, language) => {
  121. if (language && language.trim() !== '') {
  122. try {
  123. return hljs.highlight(code, { language: language.trim() }).value
  124. } catch (e) {
  125. // 如果指定的语言不支持,回退到自动检测
  126. return hljs.highlightAuto(code).value
  127. }
  128. }
  129. return hljs.highlightAuto(code).value
  130. }
  131. return (
  132. text
  133. // 处理带有语言标识的代码块
  134. .replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
  135. const highlightedCode = highlightCode(code, lang || '')
  136. return `<pre style="background-color: #282c34; padding: 10px; border-radius: 5px; overflow-x: auto;"><code class="hljs ${lang || ''}">${highlightedCode}</code></pre>`
  137. })
  138. // 处理不带语言标识的代码块
  139. .replace(/```([\s\S]*?)```/g, (match, code) => {
  140. const highlightedCode = highlightCode(code, '')
  141. return `<pre style="background-color: #282c34; padding: 10px; border-radius: 5px; overflow-x: auto;"><code class="hljs">${highlightedCode}</code></pre>`
  142. })
  143. // 处理行内代码
  144. .replace(
  145. /`([^`]+)`/g,
  146. (match, code) =>
  147. `<code style="background-color: #f0f0f0; padding: 2px 4px; border-radius: 3px;">${escapeHtml(code)}</code>`
  148. )
  149. // 处理标题 (h1 ~ h6)
  150. .replace(/^#{1,6}\s+(.+)$/gm, (match, content) => {
  151. const level = match.trim().split('#').length - 1
  152. return `<h${level}>${content.trim()}</h${level}>`
  153. })
  154. // 处理换行
  155. .replace(/\n/g, '<br>')
  156. // 处理连续空格
  157. .replace(/ {2,}/g, (match) => '&nbsp;'.repeat(match.length))
  158. // 处理粗体
  159. .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
  160. // 处理斜体
  161. .replace(/\*(.*?)\*/g, '<em>$1</em>')
  162. // 处理链接
  163. .replace(
  164. /\[([^\]]+)\]\(([^)]+)\)/g,
  165. '<a href="$2" target="_blank">$1</a>'
  166. )
  167. // 处理无序列表:先将每行列表项转换为 <li> 标签
  168. .replace(/^[*-]\s+(.+)$/gm, '<li>$1</li>')
  169. // 然后将连续的 <li> 包裹在 <ul> 中
  170. .replace(/(<li>.*<\/li>)/gs, '<ul>$1</ul>')
  171. // 处理有序列表:先将每行列表项转换为 <li> 标签
  172. .replace(/^\d+\.\s+(.+)$/gm, '<li>$1</li>')
  173. // 然后将连续的 <li> 包裹在 <ol> 中
  174. .replace(/(<li>.*<\/li>)/gs, '<ol>$1</ol>')
  175. // 处理分隔线
  176. .replace(/^---+$/gm, '<hr>')
  177. // 处理引用
  178. .replace(/^>\s+(.+)$/gm, '<blockquote>$1</blockquote>')
  179. )
  180. }
  181. export function buildExcelUnderstandingPrompt(data, fileName, pageInfo) {
  182. // if (!data || data.length < 2) {
  183. // return "这是一个空的Excel文件,请检查文件内容。";
  184. // }
  185. // const headers = data[0];
  186. // const rows = data.slice(1);
  187. // data[0].forEach((header, i) => {
  188. // if (!this.excelData[header]) this.excelData[header] = []
  189. // this.excelData[header].push(data[1][i])
  190. // })
  191. return `我将向你展示一个通过SheetJS库读取的Excel文件内容和一个form表单。请帮我理解这些数据:
  192. 文件名:${fileName}
  193. 列标题:${data.join(', ')}
  194. 表单内容:
  195. ${pageInfo}
  196. 要求:
  197. 1. 请根据表单中的表单项和列标题进行匹配,一定以表单为准,列标题只能匹配一次,没有和列标题匹配到的表单项不返回!
  198. 2. 生成表单项与列标题对应的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我excel文件中列标题的值。
  199. 3. 表单项有id根据id匹配,findBy是id,并通过findByValue告诉我id的值,没有id根据label匹配,findBy是label,并通过findByValue给我label元素的值,没有label根据placeholder匹配,findBy是placeholder,并通过findByValue告诉我placeholder的值,没有placeholder,再根据其他内容匹配
  200. 3. 并去除没有匹配到的表单项和excel文件中没有匹配到的列,
  201. 4. 通过type字段告诉我输入项的类型
  202. 5. 仅返回数组,不要返回任何其他内容。`
  203. }
  204. // 5. 如果表单项有label标签,同时返回label, 通过label字段告诉我label元素的文本
  205. export function buildObjPrompt(obj, pageInfo) {
  206. return `我将向你展示一个对象和一个form表单。请帮我理解这些数据:
  207. 对象:${JSON.stringify(obj)}
  208. 表单内容:
  209. ${pageInfo}
  210. 要求:
  211. 1. 请根据表单中的表单项和对象进行匹配,一定以表单为准,key同时在对象和表单中出现,才能返回!
  212. 2. 表单项有label,根据label匹配,没有label根据placeholder匹配,没有placeholder,根据id匹配,再根据其他内容匹配
  213. 3. 并根据实际可操作的表单项的所有信息与上传的对象的key进行匹配,生成表单项与key的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我对应的key值。在一个字段内返回
  214. 4. 并去除没有匹配到的表单项和对象中没有匹配到的key,
  215. 5. 通过type字段告诉我输入项的类型,如果对象中key对应的是日期,type统一返回date
  216. 6. 仅返回数组,不要返回任何其他内容。`
  217. }
  218. function escapeHtml(html) {
  219. const div = document.createElement('div')
  220. div.textContent = html
  221. return div.innerHTML
  222. }