Spaces:
Running
Running
File size: 14,440 Bytes
084ae67 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
const express = require("express");
const { io } = require("socket.io-client");
const { v4: uuidv4 } = require("uuid");
const { ProxyAgent } = require("proxy-agent");
// 创建代理实例
const 代理 = new ProxyAgent();
// 获取环境变量值并赋给中文变量名
const 曲奇池 = process.env.COOKIE_POOL || '';
const 用户代理 = process.env.USER_AGENT || '';
const 全局代理 = process.env.ALL_PROXY || '';
const 应用 = express();
const 端口 = process.env.PORT || 7860;
// 解析曲奇池,支持单个或多个曲奇(用逗号分隔)
const cookiePool = 曲奇池
? 曲奇池.split(',').map(cookie => cookie.trim()).filter(cookie => cookie.length > 0)
: [];
if (cookiePool.length === 0) {
console.error("错误:环境变量 曲奇池 未设置或为空。");
process.exit(1);
}
// 创建事件流的工具函数
function 创建事件(事件, 数据) {
if (typeof 数据 === "object") {
数据 = JSON.stringify(数据);
}
return `event: ${事件}\ndata: ${数据}\n\n`;
}
// 处理 POST 请求的路由 /v1/messages
应用.post("/v1/messages", (请求, 响应) => {
请求.rawBody = "";
请求.setEncoding("utf8");
// 收集请求的原始数据
请求.on("data", function (片段) {
请求.rawBody += 片段;
});
请求.on("end", async () => {
响应.setHeader("Content-Type", "text/event-stream;charset=utf-8");
try {
let 请求数据 = JSON.parse(请求.rawBody);
if (请求数据.stream == false) {
// 当 stream 为 false 时,返回固定的响应
响应.send(
JSON.stringify({
id: uuidv4(),
content: [
{
text: "请开启流式传输。",
},
{
id: "string",
name: "string",
input: {},
},
],
model: "string",
stop_reason: "end_turn",
stop_sequence: "string",
usage: {
input_tokens: 0,
output_tokens: 0,
},
})
);
} else if (请求数据.stream == true) {
// 处理 stream 为 true 的情况
let 用户消息 = [{ 问题: "", 回答: "" }];
let 最后更新 = true;
if (请求数据.system) {
// 将系统消息添加到消息列表的首位
请求数据.messages.unshift({ role: "system", content: 请求数据.system });
}
console.log(请求数据.messages);
// 遍历消息列表,构建用户和助手的问答对
请求数据.messages.forEach((消息) => {
if (消息.role == "system" || 消息.role == "user") {
if (最后更新) {
用户消息[用户消息.length - 1].问题 += 消息.content + "\n";
} else if (用户消息[用户消息.length - 1].问题 == "") {
用户消息[用户消息.length - 1].问题 += 消息.content + "\n";
} else {
用户消息.push({ 问题: 消息.content + "\n", 回答: "" });
}
最后更新 = true;
} else if (消息.role == "assistant") {
if (!最后更新) {
用户消息[用户消息.length - 1].回答 += 消息.content + "\n";
} else if (用户消息[用户消息.length - 1].回答 == "") {
用户消息[用户消息.length - 1].回答 += 消息.content + "\n";
} else {
用户消息.push({ 问题: "", 回答: 消息.content + "\n" });
}
最后更新 = false;
}
});
// 查找最新的用户消息
const 最新用户消息 = 请求数据.messages
.slice().reverse()
.find(消息 => 消息.role === "user");
if (!最新用户消息) {
throw new Error("请求中未找到用户消息。");
}
// 获取最新用户消息的索引
const 最新用户索引 = 请求数据.messages.lastIndexOf(最新用户消息);
// 构建除最新用户消息外的所有消息
const 先前消息列表 = 请求数据.messages.slice(0, 最新用户索引);
// 改进上下文拼接格式,增加提示并避免重复
let 先前消息 = 先前消息列表
.map((消息) => {
if (消息.role === "user") {
return `用户: ${消息.content}`;
} else if (消息.role === "assistant") {
return `助手: ${消息.content}`;
} else if (消息.role === "system") {
return `系统: ${消息.content}`;
}
return 消息.content;
})
.join("\n\n");
// 在上下文末尾添加提示,不再重复添加最新的用户消息
先前消息 += `\n\n# ↓请关注用户最新的消息↓\n\n`;
// 追加最新的用户消息
先前消息 += `用户: ${最新用户消息.content}`;
let 消息ID = uuidv4();
// 发送消息开始的事件
响应.write(
创建事件("message_start", {
type: "message_start",
message: {
id: 消息ID,
type: "message",
role: "assistant",
content: [],
model: "claude-3-opus-20240229",
stop_reason: null,
stop_sequence: null,
usage: { input_tokens: 8, output_tokens: 1 },
},
})
);
响应.write(创建事件("content_block_start", { type: "content_block_start", index: 0, content_block: { type: "text", text: "" } }));
响应.write(创建事件("ping", { type: "ping" }));
// 随机选择一个曲奇
const 随机曲奇 = cookiePool[Math.floor(Math.random() * cookiePool.length)];
// 配置 Socket.IO 客户端选项
var 选项 = {
agent: 代理,
auth: {
jwt: "anonymous-ask-user",
},
reconnection: false,
transports: ["websocket"],
path: "/socket.io",
hostname: "www.perplexity.ai",
secure: true,
port: "443",
extraHeaders: {
Cookie: 随机曲奇, // 使用随机选择的曲奇
"User-Agent": 用户代理,
Accept: "*/*",
priority: "u=1, i",
Referer: "https://www.perplexity.ai/",
},
};
// 通过 Socket.IO 连接 Perplexity.ai 的 WebSocket
var socket = io("wss://www.perplexity.ai/", 选项);
socket.on("connect", function () {
console.log(" > [已连接]");
socket
.emitWithAck("perplexity_ask", 先前消息, {
"version": "2.9",
"source": "default",
"attachments": [],
"language": "en-GB",
"timezone": "Europe/London",
"search_focus": "writing",
"frontend_uuid": uuidv4(),
"mode": "concise",
"is_related_query": false,
"is_default_related_query": false,
"visitor_id": uuidv4(),
"frontend_context_uuid": uuidv4(),
"prompt_source": "user",
"query_source": "home"
})
.then((响应数据) => {
console.log(响应数据);
响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
响应.write(
创建事件("message_delta", {
type: "message_delta",
delta: { stop_reason: "end_turn", stop_sequence: null },
usage: { output_tokens: 12 },
})
);
响应.write(创建事件("message_stop", { type: "message_stop" }));
响应.end();
}).catch((错误) => {
if(错误.message != "socket has been disconnected"){
console.log(错误);
}
});
});
// 监听所有事件,记录日志
socket.onAny((事件, ...参数) => {
console.log(`> [收到事件: ${事件}]`);
});
// 监听查询进度事件,处理返回的文本块
socket.on("query_progress", (数据) => {
if(数据.text){
try {
var 文本 = JSON.parse(数据.text)
var 块 = 文本.chunks[文本.chunks.length - 1];
if (块) {
let 块JSON = JSON.stringify({
type: "content_block_delta",
index: 0,
delta: { type: "text_delta", text: 块 },
});
响应.write(创建事件("content_block_delta", 块JSON));
}
} catch (错误) {
console.log("解析 query_progress 数据时出错:", 错误);
}
}
});
// 监听断开连接事件
socket.on("disconnect", function () {
console.log(" > [已断开连接]");
});
// 监听错误事件,返回错误信息给客户端
socket.on("error", (错误) => {
let 块JSON = JSON.stringify({
type: "content_block_delta",
index: 0,
delta: { type: "text_delta", text: "获取输出时出现错误\n请查看日志以获取更多信息" },
});
响应.write(创建事件("content_block_delta", 块JSON));
响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
响应.write(
创建事件("message_delta", {
type: "message_delta",
delta: { stop_reason: "end_turn", stop_sequence: null },
usage: { output_tokens: 12 },
})
);
响应.write(创建事件("message_stop", { type: "message_stop" }));
响应.end();
console.log(错误);
});
// 监听连接错误事件,返回错误信息给客户端
socket.on("connect_error", function (错误) {
let 块JSON = JSON.stringify({
type: "content_block_delta",
index: 0,
delta: { type: "text_delta", text: "连接到 Perplexity.ai 失败\n请查看日志以获取更多信息" },
});
响应.write(创建事件("content_block_delta", 块JSON));
响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
响应.write(
创建事件("message_delta", {
type: "message_delta",
delta: { stop_reason: "end_turn", stop_sequence: null },
usage: { output_tokens: 12 },
})
);
响应.write(创建事件("message_stop", { type: "message_stop" }));
响应.end();
console.log(错误);
});
// 监听客户端关闭事件,断开 Socket 连接
响应.on("close", function () {
console.log(" > [客户端已关闭连接]");
socket.disconnect();
});
} else {
throw new Error("无效的请求");
}
} catch (错误) {
console.log(错误);
响应.write(JSON.stringify({ error: 错误.message }));
响应.end();
return;
}
});
});
// 处理所有未定义的路由,返回 418
应用.use((请求, 响应, 下一个) => {
响应.status(418).send("418 I'm a teapot");
});
// 启动服务器,监听指定端口
应用.listen(端口, () => {
console.log(`Perplexity 代理服务器正在监听端口 ${端口}`);
});
|