UNPKG

esp-ai-plugin-llm-dify

Version:

让 ESP-AI 支持 dify

185 lines (159 loc) 8.19 kB
// 保存会话ID const sid = new Map([]); module.exports = { // 插件名字 name: "esp-ai-plugin-llm-dify", // 插件类型 LLM | TTS | IAT type: "LLM", main({ devLog, device_id, is_pre_connect, llm_config, text, llmServerErrorCb, llm_init_messages = [], llm_historys = [], cb, llm_params_set, logWSServer, connectServerBeforeCb, connectServerCb, log }) { try { const { api_key, url = 'https://api.dify.ai/v1', ...other_config } = llm_config; if (!api_key) return log.error(`请配置 LLM 的 api_key 参数。`); // 预请求处理 async function preConnect() { // 预连接逻辑,可以为空 } if (is_pre_connect) { preConnect(); return; } // 消息关闭标志 let shouldClose = false; // 文本结构定义(固定写法) const texts = { all_text: "", count_text: "", index: 0, }; // 告诉框架要开始连接LLM服务了 connectServerBeforeCb(); async function main() { try { // 构建请求headers const headers = { 'Authorization': `Bearer ${api_key}`, 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream', 'User-Agent': 'esp-ai-plugin-llm-dify-client/1.0.5' }; // 构建请求体 const requestBody = { inputs: {}, query: text, response_mode: "streaming", user: device_id, conversation_id: sid.get(device_id), }; // 处理历史消息(如果有) if (llm_historys && llm_historys.length > 0) { devLog && log.llm_info('添加历史消息,数量:', llm_historys.length); // 如果Dify需要特定格式的历史消息,可以在这里转换 // 注意:根据Dify文档调整这部分 } // 确保URL格式正确(去除可能的尾部斜杠) const baseUrl = url.endsWith('/') ? url.slice(0, -1) : url; // 输出调试信息 devLog && log.llm_info('请求URL:', `${baseUrl}/chat-messages`); devLog && log.llm_info('请求头:', JSON.stringify(headers)); devLog && log.llm_info('请求体:', JSON.stringify(requestBody)); // 发起请求 const response = await fetch(`${baseUrl}/chat-messages`, { method: 'POST', headers: headers, body: JSON.stringify(requestBody), // 添加超时处理 timeout: other_config.timeout || 30000 }); if (!response.ok) { const errorText = await response.text(); log.error(`Dify API错误: ${response.status} ${response.statusText}`); log.error(`错误详情: ${errorText}`); log.error(`请求URL: ${baseUrl}/chat-messages`); log.error(`请求体: ${JSON.stringify(requestBody)}`); try { const errorJson = JSON.parse(errorText); log.error(`错误结构: ${JSON.stringify(errorJson)}`); } catch (e) { // 不是JSON格式,已经输出了原始文本 } throw new Error(`Dify API错误: ${response.status} ${response.statusText}`); } // 通知框架已连接到LLM服务 connectServerCb(true); // 注册关闭函数 const controller = new AbortController(); logWSServer({ close: () => { connectServerCb(false); controller.abort(); shouldClose = true; } }); // 处理流式响应 const reader = response.body.getReader(); const decoder = new TextDecoder(); let first_response = true; while (true) { if (shouldClose) break; const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); // 解析SSE格式的响应 const lines = chunk.split('\n').filter(line => line.trim() !== ''); for (const line of lines) { if (line.startsWith('data:')) { if (line.includes('[DONE]')) continue; try { const data = JSON.parse(line.slice(5).trim()); const chunk_text = data.answer || ''; // 获取会话ID(通常在第一个响应中) if (first_response && data.conversation_id) { first_response = false; devLog && log.llm_info('获取到新会话ID:', data.conversation_id); sid.set(device_id, data.conversation_id) // 通知框架保存会话ID cb({ text, texts, chunk_text, }); } else { devLog === 2 && log.llm_info('LLM 输出:', chunk_text); texts["count_text"] += chunk_text; cb({ text, texts, chunk_text }); } } catch (e) { // 忽略解析错误 devLog && log.error('解析响应出错:', e, line); } } } } if (shouldClose) return; // 通知框架响应结束,并传递会话ID cb({ text, is_over: true, texts, shouldClose, }); // 通知框架关闭了与LLM服务的连接 connectServerCb(false); devLog && log.llm_info('==='); devLog && log.llm_info(texts["count_text"]); devLog && log.llm_info('==='); devLog && log.llm_info('LLM connect close!\n'); } catch (error) { console.log('完整错误信息:', error); llmServerErrorCb("Dify LLM 报错: " + (error.message || error)); connectServerCb(false); } } main(); } catch (err) { console.log(err); log.error("Dify LLM 插件错误:", err); connectServerCb(false); } } }