Spaces:
Running
Running
const express = require("express"); | |
const { io } = require("socket.io-client"); | |
const { v4: uuidv4 } = require("uuid"); | |
const { ProxyAgent } = require("proxy-agent"); | |
const agent = new ProxyAgent(); | |
const app = express(); | |
const port = process.env.PORT || 8081; | |
function getAuthKey(req) { | |
const authHeader = req.headers['authorization']; | |
if (authHeader && authHeader.startsWith('Bearer ')) { | |
return authHeader.slice(7); | |
} | |
return null; | |
}; | |
var opts = { | |
agent: agent, | |
auth: { | |
jwt: "anonymous-ask-user", | |
}, | |
reconnection: false, | |
transports: ["websocket"], | |
path: "/socket.io", | |
hostname: "www.perplexity.ai", | |
secure: true, | |
port: "443", | |
extraHeaders: { | |
Cookie: process.env.PPLX_COOKIE, | |
"User-Agent": process.env.USER_AGENT, | |
Accept: "*/*", | |
priority: "u=1, i", | |
Referer: "https://www.perplexity.ai/", | |
}, | |
}; | |
app.post("/api/v1/messages", (req, res) => { | |
const authKey = getAuthKey(req); | |
if (authKey !== process.env.AUTH_KEY) { | |
return res.status(401).json({ error: 'Unauthorized' }); | |
}; | |
req.rawBody = ""; | |
req.setEncoding("utf8"); | |
req.on("data", function (chunk) { | |
req.rawBody += chunk; | |
}); | |
req.on("end", async () => { | |
res.setHeader("Content-Type", "text/event-stream;charset=utf-8"); | |
try { | |
let jsonBody = JSON.parse(req.rawBody); | |
if (jsonBody.stream == false) { | |
res.send( | |
JSON.stringify({ | |
id: uuidv4(), | |
content: [ | |
{ | |
text: "Please turn on streaming.", | |
}, | |
{ | |
id: "string", | |
name: "string", | |
input: {}, | |
}, | |
], | |
model: "string", | |
stop_reason: "end_turn", | |
stop_sequence: "string", | |
usage: { | |
input_tokens: 0, | |
output_tokens: 0, | |
}, | |
}) | |
); | |
} else if (jsonBody.stream == true) { | |
// 计算用户消息长度 | |
let userMessage = [{ question: "", answer: "" }]; | |
let userQuery = ""; | |
let lastUpdate = true; | |
if (jsonBody.system) { | |
// 把系统消息加入messages的首条 | |
jsonBody.messages.unshift({ role: "system", content: jsonBody.system }); | |
} | |
console.log(jsonBody.messages); | |
jsonBody.messages.forEach((msg) => { | |
if (msg.role == "system" || msg.role == "user") { | |
if (lastUpdate) { | |
userMessage[userMessage.length - 1].question += msg.content + "\n"; | |
} else if (userMessage[userMessage.length - 1].question == "") { | |
userMessage[userMessage.length - 1].question += msg.content + "\n"; | |
} else { | |
userMessage.push({ question: msg.content + "\n", answer: "" }); | |
} | |
lastUpdate = true; | |
} else if (msg.role == "assistant") { | |
if (!lastUpdate) { | |
userMessage[userMessage.length - 1].answer += msg.content + "\n"; | |
} else if (userMessage[userMessage.length - 1].answer == "") { | |
userMessage[userMessage.length - 1].answer += msg.content + "\n"; | |
} else { | |
userMessage.push({ question: "", answer: msg.content + "\n" }); | |
} | |
lastUpdate = false; | |
} | |
}); | |
// user message to plaintext | |
let previousMessages = jsonBody.messages | |
.map((msg) => { | |
return msg.content | |
}) | |
.join("\n\n"); | |
let msgid = uuidv4(); | |
// send message start | |
res.write( | |
createEvent("message_start", { | |
type: "message_start", | |
message: { | |
id: msgid, | |
type: "message", | |
role: "assistant", | |
content: [], | |
model: "claude-3-opus-20240229", | |
stop_reason: null, | |
stop_sequence: null, | |
usage: { input_tokens: 8, output_tokens: 1 }, | |
}, | |
}) | |
); | |
res.write(createEvent("content_block_start", { type: "content_block_start", index: 0, content_block: { type: "text", text: "" } })); | |
res.write(createEvent("ping", { type: "ping" })); | |
// proxy response | |
var socket = io("wss://www.perplexity.ai/", opts); | |
socket.on("connect", function () { | |
console.log(" > [Connected]"); | |
socket | |
.emitWithAck("perplexity_ask", previousMessages, { | |
"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((response) => { | |
console.log(response); | |
res.write(createEvent("content_block_stop", { type: "content_block_stop", index: 0 })); | |
res.write( | |
createEvent("message_delta", { | |
type: "message_delta", | |
delta: { stop_reason: "end_turn", stop_sequence: null }, | |
usage: { output_tokens: 12 }, | |
}) | |
); | |
res.write(createEvent("message_stop", { type: "message_stop" })); | |
res.end(); | |
}).catch((error) => { | |
if(error.message != "socket has been disconnected"){ | |
console.log(error); | |
} | |
}); | |
}); | |
socket.onAny((event, ...args) => { | |
console.log(`> [got ${event}]`); | |
}); | |
socket.on("query_progress", (data) => { | |
if(data.text){ | |
var text = JSON.parse(data.text) | |
var chunk = text.chunks[text.chunks.length - 1]; | |
if(chunk){ | |
chunkJSON = JSON.stringify({ | |
type: "content_block_delta", | |
index: 0, | |
delta: { type: "text_delta", text: chunk }, | |
}); | |
res.write(createEvent("content_block_delta", chunkJSON)); | |
} | |
} | |
}); | |
socket.on("disconnect", function () { | |
console.log(" > [Disconnected]"); | |
}); | |
socket.on("error", (error) => { | |
chunkJSON = JSON.stringify({ | |
type: "content_block_delta", | |
index: 0, | |
delta: { type: "text_delta", text: "Error occured while fetching output 输出时出现错误\nPlease refer to the log for more information 请查看日志以获取更多信息" }, | |
}); | |
res.write(createEvent("content_block_delta", chunkJSON)); | |
res.write(createEvent("content_block_stop", { type: "content_block_stop", index: 0 })); | |
res.write( | |
createEvent("message_delta", { | |
type: "message_delta", | |
delta: { stop_reason: "end_turn", stop_sequence: null }, | |
usage: { output_tokens: 12 }, | |
}) | |
); | |
res.write(createEvent("message_stop", { type: "message_stop" })); | |
res.end(); | |
console.log(error); | |
}); | |
socket.on("connect_error", function (error) { | |
chunkJSON = JSON.stringify({ | |
type: "content_block_delta", | |
index: 0, | |
delta: { type: "text_delta", text: "Failed to connect to the Perplexity.ai 连接到Perplexity失败\nPlease refer to the log for more information 请查看日志以获取更多信息" }, | |
}); | |
res.write(createEvent("content_block_delta", chunkJSON)); | |
res.write(createEvent("content_block_stop", { type: "content_block_stop", index: 0 })); | |
res.write( | |
createEvent("message_delta", { | |
type: "message_delta", | |
delta: { stop_reason: "end_turn", stop_sequence: null }, | |
usage: { output_tokens: 12 }, | |
}) | |
); | |
res.write(createEvent("message_stop", { type: "message_stop" })); | |
res.end(); | |
console.log(error); | |
}); | |
res.on("close", function () { | |
console.log(" > [Client closed]"); | |
socket.disconnect(); | |
}); | |
} else { | |
throw new Error("Invalid request"); | |
} | |
} catch (e) { | |
console.log(e); | |
res.write(JSON.stringify({ error: e.message })); | |
res.end(); | |
return; | |
} | |
}); | |
}); | |
// handle other | |
app.use((req, res, next) => { | |
res.status(404).send("Not Found"); | |
}); | |
app.listen(port, () => { | |
console.log(`Perplexity proxy listening on port ${port}`); | |
}); | |
// eventStream util | |
function createEvent(event, data) { | |
// if data is object, stringify it | |
if (typeof data === "object") { | |
data = JSON.stringify(data); | |
} | |
return `event: ${event}\ndata: ${data}\n\n`; | |
} |