'use strict'; const axios = require('axios'); const DOCUMENT_EXTENSIONS = ['pdf', 'doc', 'docx']; const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp']; class AiDify { /** * 构造函数 * @param {object} config * @param {string} config.apiKey - Dify API Key * @param {string} config.chatflowId - ChatFlow ID * @param {string} [config.baseUrl=https://api.dify.ai/v1] - Dify API 接口地址 */ constructor({ apiKey = 'app-fiJr1pNxjdKXpetko8ANawnY', chatflowId = '', baseUrl = 'http://workflow.smartcost.com.cn/v1' }) { if (!apiKey) { throw new Error('apiKey 不能为空'); } this.apiKey = apiKey; this.chatflowId = chatflowId; this.baseUrl = baseUrl; } /** * 向 ChatFlow 发送请求 * @param {string} userInput - 用户输入内容 * @param {string} [userId='user_123'] - 用户 ID * @returns {Promise} - Dify 返回的对象 */ async ask(userInput, userId = 'user_123') { const url = `${this.baseUrl}/chat-messages`; const headers = { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }; const body = { inputs: {}, // 如果你的 ChatFlow 有 schema,可填写 key-value query: userInput, response_mode: 'blocking', user: userId, conversation_id: this.chatflowId, }; try { const res = await axios.post(url, body, { headers }); return res.data; } catch (err) { console.error('Dify 请求失败:', err.response || err.response.data || err.message); throw new Error('Dify 请求失败'); } } async workflows(body) { const url = `${this.baseUrl}/workflows/run`; const headers = { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }; try { const res = await axios.post(url, body, { headers }); return res.data.data.outputs; } catch (err) { console.error('Dify 请求失败:', err.response || err.response.data || err.message); throw new Error('Dify 请求失败'); } } async posCalcDetailRead(col, billsName, keyword, files, userId = `user_123`) { const body = { inputs: { header: col.map(x => { return x.title + (x.unit ? `(${x.unit})` : '') }).join(','), billsName, keyword, imgList: [], }, user: userId, }; for (const f of files) { body.inputs.imgList.push({ type: "image", transfer_method: "remote_url", url: f.viewpath, }) } return await this.workflows(body); } async imageTransExcel(files, userId = `user_123`) { const body = { inputs: { imgList: [] }, user: userId, response_mode: 'blocking', }; for (const f of files) { body.inputs.imgList.push({ type: "image", transfer_method: "remote_url", url: f.viewpath, }) } return await this.workflows(body); } async billsIdentify(name, unit, userId = `user_123`) { const body = { inputs: { billsName: name || '' + ( unit ? `(${unit})` : '') }, user: userId, response_mode: 'blocking', }; return await this.workflows(body); } /** * 根据文件名后缀判断 Dify 的 type 字段(image / document) */ resolveDifyFileType(filename = '') { const ext = filename ? filename.split('.').pop().toLowerCase() : ''; if (IMAGE_EXTENSIONS.includes(ext)) return 'image'; if (DOCUMENT_EXTENSIONS.includes(ext)) return 'document'; // 无法判断时默认按document处理,让Dify那边自行兜底 return 'document'; } async contractTrans(file, userId = 'user_123') { const body = { inputs: { fileList: [] }, user: userId, response_mode: 'blocking', }; if (!file.viewpath) { throw new Error('文件缺少viewpath字段'); } const fileType = this.resolveDifyFileType(file.filename); body.inputs.fileList.push({ type: fileType, transfer_method: 'remote_url', url: file.viewpath, }); return await this.workflows(body); } } module.exports = AiDify;