| | 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 crypto = require('crypto'); |
| |
|
| | const app = express(); |
| | const port = process.env.PORT || 8081; |
| |
|
| | 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/", |
| | }, |
| | }; |
| |
|
| | |
| | function validateApiToken(req, res, next) { |
| | const apiToken = req.headers['authorization']; |
| | if (!apiToken || !apiToken.startsWith('Bearer ') || apiToken.split(' ')[1] !== process.env.API_TOKEN) { |
| | return res.status(401).json({ error: 'Unauthorized' }); |
| | } |
| | next(); |
| | } |
| |
|
| | |
| | app.use(validateApiToken); |
| |
|
| | |
| | app.use((req, res, next) => { |
| | console.log(`Received ${req.method} request to ${req.url}`); |
| | next(); |
| | }); |
| |
|
| | app.post("/hf/v1/chat/completions", (req, res) => { |
| | console.log("Received request to /hf/v1/chat/completions"); |
| | req.rawBody = ""; |
| | req.setEncoding("utf8"); |
| |
|
| | req.on("data", function (chunk) { |
| | req.rawBody += chunk; |
| | }); |
| |
|
| | req.on("end", async () => { |
| | try { |
| | let jsonBody = JSON.parse(req.rawBody); |
| | console.log("Parsed request body:", jsonBody); |
| | |
| | if (jsonBody.stream !== true) { |
| | |
| | res.json({ |
| | id: "chatcmpl-" + crypto.randomBytes(16).toString('hex'), |
| | object: "chat.completion", |
| | created: Math.floor(Date.now() / 1000), |
| | model: "claude-3-opus-20240229", |
| | choices: [ |
| | { |
| | index: 0, |
| | message: { |
| | role: "assistant", |
| | content: "This API only supports streaming responses. Please set 'stream' to true in your request.", |
| | }, |
| | finish_reason: "stop" |
| | } |
| | ], |
| | usage: { |
| | prompt_tokens: 0, |
| | completion_tokens: 0, |
| | total_tokens: 0 |
| | } |
| | }); |
| | } else { |
| | |
| | res.setHeader("Content-Type", "text/event-stream"); |
| | res.setHeader("Cache-Control", "no-cache"); |
| | res.setHeader("Connection", "keep-alive"); |
| |
|
| | |
| | let previousMessages = jsonBody.messages |
| | .map((msg) => msg.content) |
| | .join("\n\n"); |
| |
|
| | 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); |
| | sendFinalChunk(res); |
| | }).catch((error) => { |
| | if(error.message != "socket has been disconnected"){ |
| | console.log(error); |
| | } |
| | sendFinalChunk(res); |
| | }); |
| | }); |
| |
|
| | socket.on("query_progress", (data) => { |
| | if(data.text){ |
| | var text = JSON.parse(data.text) |
| | var chunk = text.chunks[text.chunks.length - 1]; |
| | if(chunk){ |
| | sendChunk(res, chunk); |
| | } |
| | } |
| | }); |
| |
|
| | socket.on("disconnect", function () { |
| | console.log(" > [Disconnected]"); |
| | sendFinalChunk(res); |
| | }); |
| |
|
| | socket.on("error", (error) => { |
| | console.log(error); |
| | sendErrorChunk(res, "Error occurred while fetching output. Please refer to the log for more information."); |
| | sendFinalChunk(res); |
| | }); |
| |
|
| | socket.on("connect_error", function (error) { |
| | console.log(error); |
| | sendErrorChunk(res, "Failed to connect to Perplexity.ai. Please refer to the log for more information."); |
| | sendFinalChunk(res); |
| | }); |
| |
|
| | res.on("close", function () { |
| | console.log(" > [Client closed]"); |
| | socket.disconnect(); |
| | }); |
| | } |
| | } catch (e) { |
| | console.error("Error processing request:", e); |
| | res.status(400).json({ error: e.message }); |
| | } |
| | }); |
| | }); |
| |
|
| | function sendChunk(res, content) { |
| | const chunk = { |
| | id: "chatcmpl-" + crypto.randomBytes(16).toString('hex'), |
| | object: "chat.completion.chunk", |
| | created: Math.floor(Date.now() / 1000), |
| | model: "claude-3-opus-20240229", |
| | choices: [{ |
| | index: 0, |
| | delta: { |
| | content: content |
| | }, |
| | finish_reason: null |
| | }] |
| | }; |
| | res.write(`data: ${JSON.stringify(chunk)}\n\n`); |
| | } |
| |
|
| | function sendErrorChunk(res, errorMessage) { |
| | const chunk = { |
| | id: "chatcmpl-" + crypto.randomBytes(16).toString('hex'), |
| | object: "chat.completion.chunk", |
| | created: Math.floor(Date.now() / 1000), |
| | model: "claude-3-opus-20240229", |
| | choices: [{ |
| | index: 0, |
| | delta: { |
| | content: errorMessage |
| | }, |
| | finish_reason: "stop" |
| | }] |
| | }; |
| | res.write(`data: ${JSON.stringify(chunk)}\n\n`); |
| | } |
| |
|
| | function sendFinalChunk(res) { |
| | const finalChunk = { |
| | id: "chatcmpl-" + crypto.randomBytes(16).toString('hex'), |
| | object: "chat.completion.chunk", |
| | created: Math.floor(Date.now() / 1000), |
| | model: "claude-3-opus-20240229", |
| | choices: [{ |
| | index: 0, |
| | delta: {}, |
| | finish_reason: "stop" |
| | }] |
| | }; |
| | res.write(`data: ${JSON.stringify(finalChunk)}\n\n`); |
| | res.write("data: [DONE]\n\n"); |
| | res.end(); |
| | } |
| |
|
| | |
| | app.use((req, res, next) => { |
| | console.log(`404 Not Found: ${req.method} ${req.url}`); |
| | res.status(404).json({ error: "Not Found" }); |
| | }); |
| |
|
| | |
| | app.use((err, req, res, next) => { |
| | console.error("Unhandled error:", err); |
| | res.status(500).json({ error: "Internal Server Error" }); |
| | }); |
| |
|
| | |
| | app.use((req, res, next) => { |
| | res.status(404).send("Not Found"); |
| | }); |
| |
|
| | app.listen(port, () => { |
| | console.log(`Perplexity proxy listening on port ${port}`); |
| | }); |
| | |
| | function createEvent(event, data) { |
| | |
| | if (typeof data === "object") { |
| | data = JSON.stringify(data); |
| | } |
| | return `event: ${event}\ndata: ${data}\n\n`; |
| | } |