Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 使用官方 Node.js 基础镜像
FROM node:16-alpine

# 设置工作目录
WORKDIR /usr/src/app

# 复制 package.json 和 package-lock.json 到工作目录
# COPY package*.json ./
COPY . ./

# 安装项目依赖
RUN npm config set registry https://registry.npmmirror.com
RUN npm install

# 暴露容器的端口
EXPOSE 3099

# 在容器启动时运行应用
CMD [ "npm", "start" ]
23 changes: 13 additions & 10 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ function parseConfig(authHeader, modelParam) {

// 方式一:所有信息都在 Authorization header 中
if (tokenParts.length >= 3) {
const [difyApiUrl, apiKey, botType, inputVariable, outputVariable] = tokenParts;
const [difyApiUrl, apiKey, botType, inputVariable, outputVariable, apiUser] = tokenParts;
config = {
DIFY_API_URL: difyApiUrl,
API_KEY: apiKey,
BOT_TYPE: botType,
INPUT_VARIABLE: inputVariable || "",
OUTPUT_VARIABLE: outputVariable || "",
INPUT_VARIABLE: inputVariable || "query",
OUTPUT_VARIABLE: outputVariable || "text",
API_USER: apiUser || "apiuser",
};
log("info", "配置解析成功 - 方式一", config);
return config;
Expand All @@ -62,7 +63,7 @@ function parseConfig(authHeader, modelParam) {
// 方式二和方式三的处理
if (tokenParts.length === 1) {
const singleValue = tokenParts[0].trim();

// 解析 model 参数
if (!modelParam) {
log("error", "缺少 model 参数");
Expand All @@ -78,21 +79,23 @@ function parseConfig(authHeader, modelParam) {
// 方式二:Authorization 是 API_KEY
if (singleValue.length > 0 && !singleValue.includes("http")) {
config.API_KEY = singleValue;
const [_, botType, difyApiUrl, inputVariable, outputVariable] = modelParts;
const [_, botType, difyApiUrl, inputVariable, outputVariable, apiUser] = modelParts;
config.DIFY_API_URL = difyApiUrl;
config.BOT_TYPE = botType;
config.INPUT_VARIABLE = inputVariable || "";
config.OUTPUT_VARIABLE = outputVariable || "";
config.INPUT_VARIABLE = inputVariable || "query";
config.OUTPUT_VARIABLE = outputVariable || "text";
config.API_USER = apiUser || "apiuser";
log("info", "配置解析成功 - 方式二", config);
}
// 方式三:Authorization 是 DIFY_API_URL
else {
config.DIFY_API_URL = singleValue;
const [_, apiKey, botType, inputVariable, outputVariable] = modelParts;
const [_, apiKey, botType, inputVariable, outputVariable, apiUser] = modelParts;
config.API_KEY = apiKey;
config.BOT_TYPE = botType;
config.INPUT_VARIABLE = inputVariable || "";
config.OUTPUT_VARIABLE = outputVariable || "";
config.INPUT_VARIABLE = inputVariable || "query";
config.OUTPUT_VARIABLE = outputVariable || "text";
config.API_USER = apiUser || "apiuser";
log("info", "配置解析成功 - 方式三", config);
}
}
Expand Down
10 changes: 5 additions & 5 deletions botType/chatHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,17 @@ async function handleRequest(req, res, config, requestId, startTime) {
body: data,
});

const userId = "apiuser"; // 如果可用,替换为实际的用户 ID
const userId = config.API_USER; // 如果可用,替换为实际的用户 ID
const lastMessage = messages[messages.length - 1];

// 第一步:先扫描所有消息中的图片内容
log("info", "开始扫描所有消息中的图片", { requestId, messageCount: messages.length });
for (const message of messages) {
if (Array.isArray(message.content)) {
for (const content of message.content) {
if (content.type === "image_url" && content.image_url && content.image_url.url) {
const imageUrl = content.image_url.url;

// 检查URL是否为base64数据
if (imageUrl.startsWith('data:')) {
// 是base64数据,需要上传
Expand Down Expand Up @@ -144,14 +144,14 @@ async function handleRequest(req, res, config, requestId, startTime) {
}
}
}

// 第二步:从最后一条消息中提取查询文本
if (Array.isArray(lastMessage.content)) {
for (const content of lastMessage.content) {
// 处理字符串类型的内容(OpenAI格式)
if (typeof content === "string") {
queryString += content + "\n";
}
}
// 处理对象类型的内容
else if (content.type === "text") {
queryString += content.text + "\n";
Expand Down
6 changes: 3 additions & 3 deletions botType/completionHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ async function handleRequest(req, res, config, requestId, startTime) {
body: data,
});

const userId = "apiuser"; // 如果可用,替换为实际的用户 ID
const userId = config.API_USER; // 如果可用,替换为实际的用户 ID

// 第一步:先扫描所有消息中的图片内容
log("info", "开始扫描所有消息中的图片", { requestId, messageCount: messages.length });
Expand All @@ -108,7 +108,7 @@ async function handleRequest(req, res, config, requestId, startTime) {
for (const content of message.content) {
if (content.type === "image_url" && content.image_url && content.image_url.url) {
const imageUrl = content.image_url.url;

// 检查URL是否为base64数据
if (imageUrl.startsWith('data:')) {
// 是base64数据,需要上传
Expand Down Expand Up @@ -148,7 +148,7 @@ async function handleRequest(req, res, config, requestId, startTime) {
// 处理字符串类型的内容(OpenAI格式)
if (typeof content === "string") {
queryString += content + "\n";
}
}
// 处理对象类型的内容
else if (content.type === "text") {
queryString += content.text + "\n";
Expand Down
95 changes: 81 additions & 14 deletions botType/workflowHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,26 @@ async function handleRequest(req, res, config, requestId, startTime) {
body: data,
});

const userId = "apiuser"; // 如果可用,替换为实际的用户 ID
const userId = config.API_USER; // 如果可用,替换为实际的用户 ID
const lastMessage = messages[messages.length - 1];

// 第一步:先扫描所有消息中的图片内容
log("info", "开始扫描所有消息中的图片", { requestId, messageCount: messages.length });
for (const message of messages) {
if (Array.isArray(message.content)) {
for (const content of message.content) {
for (let content of message.content) {
try {
content = JSON.parse(content);
} catch (error) {
// nothing
}
let fileKeyName = "file_input";
if (content.key !== undefined && typeof content.key === "string") {
fileKeyName = content.key;
}
if (content.type === "image_url" && content.image_url && content.image_url.url) {
const imageUrl = content.image_url.url;

// 检查URL是否为base64数据
if (imageUrl.startsWith('data:')) {
// 是base64数据,需要上传
Expand All @@ -127,7 +136,7 @@ async function handleRequest(req, res, config, requestId, startTime) {
upload_file_id: fileId,
type: fileType,
};
inputs["file_input"] = fileInput;
inputs[fileKeyName] = fileInput;
} else {
// 是真正的URL,直接使用remote_url方式
const fileExt = getFileExtension(imageUrl);
Expand All @@ -138,16 +147,21 @@ async function handleRequest(req, res, config, requestId, startTime) {
url: imageUrl,
type: fileType,
};
inputs["file_input"] = fileInput;
inputs[fileKeyName] = fileInput;
}
}
}
}
}

// 第二步:从最后一条消息中提取查询文本
if (Array.isArray(lastMessage.content)) {
for (const content of lastMessage.content) {
for (let content of lastMessage.content) {
try {
content = JSON.parse(content);
} catch (error) {
// nothing
}
// 处理字符串类型的内容(OpenAI格式)
if (typeof content === "string") {
// 将字符串类型的内容设置为输入变量
Expand All @@ -158,11 +172,42 @@ async function handleRequest(req, res, config, requestId, startTime) {
// 假设文本内容是输入变量,需要根据您的应用逻辑调整
inputs["text_input"] = content.text;
}
// 处理json类型的内容
else if (content.type === "json") {
if (typeof content.text === "object") {
Object.assign(inputs, content.text);
} else if (typeof content.text === "string") {
Object.assign(inputs, JSON.parse(content.text));
}
}
else if (typeof content === "object") {
Object.assign(inputs, content.text || content);
}
// 注意:这里不再重复处理image_url,因为已经在上面处理过了
}
} else {
let content = lastMessage.content;
try {
content = JSON.parse(content);
} catch (error) {
// nothing
}
// 假设消息内容是输入变量,需要根据您的应用逻辑调整
inputs["text_input"] = lastMessage.content;
if (typeof content === "object") {
if (typeof content.type !== 'undefined' && content.type === "json") {
if (typeof content.text === "object") {
Object.assign(inputs, content.text);
} else if (typeof content.text === "string") {
Object.assign(inputs, JSON.parse(content.text));
} else {
Object.assign(inputs, content);
}
} else {
Object.assign(inputs, content.text || content);
}
} else if (typeof content === "string") {
inputs["text_input"] = content;
}
}

// 日志记录
Expand Down Expand Up @@ -231,6 +276,8 @@ async function handleRequest(req, res, config, requestId, startTime) {

if (stream) {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
let buffer = "";
const responseStream = resp.body
.pipe(new PassThrough())
Expand Down Expand Up @@ -268,7 +315,30 @@ async function handleRequest(req, res, config, requestId, startTime) {
} else if (chunkObj.event === "node_finished") {
// 处理 node_finished 事件
} else if (chunkObj.event === "workflow_finished") {
const outputs = chunkObj.data.outputs;
const chunkId = `chatcmpl-${Date.now()}`;
const chunkCreated = chunkObj.created_at;
res.write(
"data: " +
JSON.stringify({
id: chunkId,
object: "chat.completion.chunk",
created: chunkCreated,
model: data.model,
choices: [
{
index: 0,
delta: {},
finish_reason: "stop",
},
],
}) +
"\n\n"
);
res.write("data: [DONE]\n\n");
res.end();
isResponseEnded = true;
} else if (chunkObj.event === "text_chunk") {
const outputs = chunkObj.data;
let result;
if (config.OUTPUT_VARIABLE) {
result = outputs[config.OUTPUT_VARIABLE];
Expand All @@ -292,15 +362,12 @@ async function handleRequest(req, res, config, requestId, startTime) {
delta: {
content: result,
},
finish_reason: "stop",
finish_reason: null,
},
],
}) +
"\n\n"
);
res.write("data: [DONE]\n\n");
res.end();
isResponseEnded = true;
}
} else if (chunkObj.event === "ping") {
// 处理 ping 事件
Expand Down