ai-service.js 9.8 KB

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