init: 初始化demo项目,新增deepseek和千问api调用demo;

This commit is contained in:
2026-04-15 16:25:05 +08:00
parent 8e8b19cce3
commit 2c1cd85e77
7 changed files with 1360 additions and 1 deletions

167
platforms/qwen/router.js Normal file
View File

@@ -0,0 +1,167 @@
const express = require("express");
const router = express.Router();
const QWEN_API_KEY = process.env.QWEN_API_KEY;
const QWEN_MODEL = process.env.QWEN_MODEL || "qwen-plus";
const QWEN_BASE_URL =
process.env.QWEN_BASE_URL ||
"https://dashscope.aliyuncs.com/compatible-mode/v1";
router.post("/chat", async (req, res) => {
try {
if (!QWEN_API_KEY) {
return res.status(500).json({
error: "缺少 QWEN_API_KEY请先在 .env 中配置",
});
}
const { messages, model, temperature } = req.body || {};
if (!Array.isArray(messages) || messages.length === 0) {
return res.status(400).json({
error: "请求体 messages 必须是非空数组",
});
}
const response = await fetch(`${QWEN_BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${QWEN_API_KEY}`,
},
body: JSON.stringify({
model: model || QWEN_MODEL,
messages,
temperature: typeof temperature === "number" ? temperature : 0.7,
}),
});
const data = await response.json();
if (!response.ok) {
return res.status(response.status).json({
error: "调用千问失败",
detail: data,
});
}
return res.json({
id: data.id,
model: data.model,
content: data?.choices?.[0]?.message?.content || "",
raw: data,
});
} catch (error) {
return res.status(500).json({
error: "服务内部错误",
detail: error.message,
});
}
});
router.post("/chat/stream", async (req, res) => {
try {
if (!QWEN_API_KEY) {
return res.status(500).json({
error: "缺少 QWEN_API_KEY请先在 .env 中配置",
});
}
const { messages, model, temperature } = req.body || {};
if (!Array.isArray(messages) || messages.length === 0) {
return res.status(400).json({
error: "请求体 messages 必须是非空数组",
});
}
res.setHeader("Content-Type", "text/event-stream; charset=utf-8");
res.setHeader("Cache-Control", "no-cache, no-transform");
res.setHeader("Connection", "keep-alive");
res.flushHeaders();
const controller = new AbortController();
req.on("close", () => {
controller.abort();
});
const response = await fetch(`${QWEN_BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${QWEN_API_KEY}`,
},
body: JSON.stringify({
model: model || QWEN_MODEL,
messages,
temperature: typeof temperature === "number" ? temperature : 0.7,
stream: true,
}),
signal: controller.signal,
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
res.write(
`data: ${JSON.stringify({
error: "调用千问流式接口失败",
detail: errorData,
})}\n\n`,
);
return res.end();
}
if (!response.body) {
res.write(`data: ${JSON.stringify({ error: "未获取到流式响应体" })}\n\n`);
return res.end();
}
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith("data:")) continue;
const payload = trimmed.slice(5).trim();
if (payload === "[DONE]") {
res.write("data: [DONE]\n\n");
res.end();
return;
}
try {
const parsed = JSON.parse(payload);
const delta = parsed?.choices?.[0]?.delta?.content;
if (delta) {
res.write(`data: ${JSON.stringify({ delta })}\n\n`);
}
} catch (_error) {
// 忽略非 JSON 片段,继续解析后续流内容
}
}
}
res.write("data: [DONE]\n\n");
return res.end();
} catch (error) {
if (!res.headersSent) {
return res.status(500).json({
error: "服务内部错误",
detail: error.message,
});
}
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
return res.end();
}
});
module.exports = router;