ai_dify.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. 'use strict';
  2. const axios = require('axios');
  3. const DOCUMENT_EXTENSIONS = ['pdf', 'doc', 'docx'];
  4. const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];
  5. class AiDify {
  6. /**
  7. * 构造函数
  8. * @param {object} config
  9. * @param {string} config.apiKey - Dify API Key
  10. * @param {string} config.chatflowId - ChatFlow ID
  11. * @param {string} [config.baseUrl=https://api.dify.ai/v1] - Dify API 接口地址
  12. */
  13. constructor({ apiKey = 'app-fiJr1pNxjdKXpetko8ANawnY', chatflowId = '', baseUrl = 'http://workflow.smartcost.com.cn/v1' }) {
  14. if (!apiKey) {
  15. throw new Error('apiKey 不能为空');
  16. }
  17. this.apiKey = apiKey;
  18. this.chatflowId = chatflowId;
  19. this.baseUrl = baseUrl;
  20. }
  21. /**
  22. * 向 ChatFlow 发送请求
  23. * @param {string} userInput - 用户输入内容
  24. * @param {string} [userId='user_123'] - 用户 ID
  25. * @returns {Promise<object>} - Dify 返回的对象
  26. */
  27. async ask(userInput, userId = 'user_123') {
  28. const url = `${this.baseUrl}/chat-messages`;
  29. const headers = {
  30. 'Authorization': `Bearer ${this.apiKey}`,
  31. 'Content-Type': 'application/json',
  32. };
  33. const body = {
  34. inputs: {}, // 如果你的 ChatFlow 有 schema,可填写 key-value
  35. query: userInput,
  36. response_mode: 'blocking',
  37. user: userId,
  38. conversation_id: this.chatflowId,
  39. };
  40. try {
  41. const res = await axios.post(url, body, { headers });
  42. return res.data;
  43. } catch (err) {
  44. console.error('Dify 请求失败:', err.response || err.response.data || err.message);
  45. throw new Error('Dify 请求失败');
  46. }
  47. }
  48. async workflows(body) {
  49. const url = `${this.baseUrl}/workflows/run`;
  50. const headers = {
  51. 'Authorization': `Bearer ${this.apiKey}`,
  52. 'Content-Type': 'application/json',
  53. };
  54. try {
  55. const res = await axios.post(url, body, { headers });
  56. return res.data.data.outputs;
  57. } catch (err) {
  58. console.error('Dify 请求失败:', err.response || err.response.data || err.message);
  59. throw new Error('Dify 请求失败');
  60. }
  61. }
  62. async posCalcDetailRead(col, billsName, keyword, files, userId = `user_123`) {
  63. const body = {
  64. inputs: {
  65. header: col.map(x => { return x.title + (x.unit ? `(${x.unit})` : '') }).join(','),
  66. billsName,
  67. keyword,
  68. imgList: [],
  69. },
  70. user: userId,
  71. };
  72. for (const f of files) {
  73. body.inputs.imgList.push({
  74. type: "image",
  75. transfer_method: "remote_url",
  76. url: f.viewpath,
  77. })
  78. }
  79. return await this.workflows(body);
  80. }
  81. async imageTransExcel(files, userId = `user_123`) {
  82. const body = {
  83. inputs: { imgList: [] },
  84. user: userId,
  85. response_mode: 'blocking',
  86. };
  87. for (const f of files) {
  88. body.inputs.imgList.push({
  89. type: "image",
  90. transfer_method: "remote_url",
  91. url: f.viewpath,
  92. })
  93. }
  94. return await this.workflows(body);
  95. }
  96. async billsIdentify(name, unit, userId = `user_123`) {
  97. const body = {
  98. inputs: { billsName: name || '' + ( unit ? `(${unit})` : '') },
  99. user: userId,
  100. response_mode: 'blocking',
  101. };
  102. return await this.workflows(body);
  103. }
  104. /**
  105. * 根据文件名后缀判断 Dify 的 type 字段(image / document)
  106. */
  107. resolveDifyFileType(filename = '') {
  108. const ext = filename ? filename.split('.').pop().toLowerCase() : '';
  109. if (IMAGE_EXTENSIONS.includes(ext)) return 'image';
  110. if (DOCUMENT_EXTENSIONS.includes(ext)) return 'document';
  111. // 无法判断时默认按document处理,让Dify那边自行兜底
  112. return 'document';
  113. }
  114. async contractTrans(file, userId = 'user_123') {
  115. const body = {
  116. inputs: { fileList: [] },
  117. user: userId,
  118. response_mode: 'blocking',
  119. };
  120. if (!file.viewpath) {
  121. throw new Error('文件缺少viewpath字段');
  122. }
  123. const fileType = this.resolveDifyFileType(file.filename);
  124. body.inputs.fileList.push({
  125. type: fileType,
  126. transfer_method: 'remote_url',
  127. url: file.viewpath,
  128. });
  129. return await this.workflows(body);
  130. }
  131. }
  132. module.exports = AiDify;