mirror of
https://github.com/PawanOsman/ChatGPT.git
synced 2026-06-02 06:14:25 +02:00
Rewrite the project
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
.vscode
|
||||
.idea
|
||||
node_modules
|
||||
@@ -3,8 +3,12 @@
|
||||
.DS_Store
|
||||
npm-debug.log
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
node_modules/
|
||||
dist/
|
||||
*.tsbuildinfo
|
||||
*.js
|
||||
*.js.map
|
||||
.parcel-cache
|
||||
db.json
|
||||
.env
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM node:18-alpine
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ["package.json", "package-lock.json*", "./"]
|
||||
|
||||
RUN npm install --production
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 3000 8080
|
||||
|
||||
CMD ["npm", "start"]
|
||||
@@ -1,229 +1,145 @@
|
||||
## [Check the new Google Bard Chatbot!](https://github.com/PawanOsman/GoogleBard)
|
||||
### If you have any questions or need assistance, please join [[Discord](https://discord.pawan.krd)]
|
||||
# ChatGPT API Free Reverse Proxy
|
||||
|
||||
# Welcome to ChatGPT API _**FREE Reverse Proxy**_
|
||||
Welcome to the **ChatGPT API Free Reverse Proxy** project, a complimentary resource allowing seamless access to OpenAI's API. This project mirrors the official OpenAI API endpoints, enabling users to leverage OpenAI functionalities without direct cost. Dive into our documentation to discover how to set up your reverse proxy or connect with our hosted service for an even smoother experience.
|
||||
|
||||
**ChatGPT API Free Reverse Proxy** is a free reverse proxy to OpenAI API that allows users to access OpenAI API for free.
|
||||
## Quick Links
|
||||
|
||||
# Table of Contents
|
||||
- [Join our Discord Community](https://discord.pawan.krd) for support and questions.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [How to use ChatGPT API Reverse Proxy](#how-to-use-chatgpt-api-reverse-proxy)
|
||||
|
||||
- [Self-Host Your Own API](#self-host-your-own-api)
|
||||
- [Use Our Hosted API](#use-our-hosted-api-reverse-proxy)
|
||||
- [Text Completion](#text-completion)
|
||||
- [Chat Completion (ChatGPT)](#chat-completion-chatgpt)
|
||||
- [Image Generation (DALL-E)](#image-generation-dall-e)
|
||||
- [Examples using OpenAI libraries](#examples-using-openai-libraries)
|
||||
- [Python](#python)
|
||||
- [Node.js](#nodejs)
|
||||
|
||||
- Option 1: [Installing/Self-Hosting Guide](#self-hosting-guide) (Without using any API key)
|
||||
- [Your PC/Server](#your-pcserver)
|
||||
- [Termux on Android Phones](#termux-on-android-phones)
|
||||
- Option 2: [Accessing Our Hosted API](#accessing-our-hosted-api) (Free)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [License](#license)
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple OpenAI Keys** - You can use multiple OpenAI keys. The API will randomly choose one of the keys to use.
|
||||
- **Moderation** - The API has a built-in moderation system that will automatically check the prompt before sending it to OpenAI API (To prevent OpenAI terminate the account for violating OpenAI's policy).
|
||||
- **Streaming Response** - The API supports streaming response, so you can get the response as soon as it's available.
|
||||
- **Same as Official** - The API has the same endpoints as the official API, so you can use the same code to access the API (even the official OpenAI libraries)
|
||||
- **Free** - The API is free to use through our [hosted API](#use-our-hosted-api) (You can also self-host the API if you want).
|
||||
- **Streaming Response**: The API supports streaming response, so you can get the response as soon as it's available.
|
||||
- **API Endpoint Compatibility**: Full alignment with official OpenAI API endpoints, ensuring hassle-free integration with existing OpenAI libraries.
|
||||
- **Complimentary Access**: No charges for API usage, making advanced AI accessible to everyone even **without an API key**.
|
||||
|
||||
**Note:** Self-hosting it isn't free, you need to use your OpenAI Account credit.
|
||||
## Installing/Self-Hosting Guide
|
||||
|
||||
## How to use ChatGPT API Reverse Proxy
|
||||
### Your PC/Server
|
||||
|
||||
You can use ChatGPT API Reverse Proxy by choosing one of the following methods:
|
||||
To install and run the ChatGPT API Reverse Proxy on your PC/Server by following these steps:
|
||||
|
||||
- [Self-Host Your Own API](#self-host-your-own-api)
|
||||
- [Use Our Hosted API](#use-our-hosted-api-reverse-proxy)
|
||||
Note: This option is not available to all countries yet. if you are from a country that is not supported, you can use a **U.S. VPN** or use **our hosted API**.
|
||||
|
||||
|
||||
1. Ensure NodeJs (v19+) is installed: [Download NodeJs](https://nodejs.org/en/download)
|
||||
2. Clone this repository:
|
||||
```bash
|
||||
git clone https://github.com/PawanOsman/ChatGPT.git
|
||||
```
|
||||
3. Open `start.bat` (Windows) or `start.sh` (Linux with `bash start.sh` command) to install dependencies and launch the server.
|
||||
4. Done, you can connect to your local server's API at:
|
||||
```
|
||||
http://localhost:3040/v1/chat/completions
|
||||
```
|
||||
Note that the base url will be `http://localhost:3040/v1`
|
||||
|
||||
# Self-Host Your Own API
|
||||
To include installation instructions for Termux on Android devices, you can add the following section right after the instructions for Linux in the **Installing/Self-Hosting Guide**:
|
||||
|
||||
To self-host your own ChatGPT API, you can use the following steps:
|
||||
### Termux on Android Phones
|
||||
|
||||
1. [Create an OpenAI API Key](https://platform.openai.com/account/api-keys)
|
||||
2. Clone this repository and install the dependencies:
|
||||
To install and run the ChatGPT API Reverse Proxy on Android using Termux, follow these steps:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/PawanOsman/ChatGPT.git
|
||||
cd ChatGPT
|
||||
npm install
|
||||
```
|
||||
1. Install [Termux](https://play.google.com/store/apps/details?id=com.termux) from the Play Store.
|
||||
2. Update Termux packages:
|
||||
```bash
|
||||
apt update
|
||||
```
|
||||
3. Upgrade Termux packages:
|
||||
```bash
|
||||
apt upgrade
|
||||
```
|
||||
4. Install git, Node.js, and npm:
|
||||
```bash
|
||||
apt install -y git nodejs
|
||||
```
|
||||
5. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/PawanOsman/ChatGPT.git
|
||||
```
|
||||
6. Navigate to the cloned directory:
|
||||
```bash
|
||||
cd ChatGPT
|
||||
```
|
||||
7. Start the server with:
|
||||
|
||||
3. Set your OpenAI key and other configurations in the `config.js` file.
|
||||
4. Start the server:
|
||||
```bash
|
||||
bash start.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
8. Your local server will now be running and accessible at:
|
||||
|
||||
4. Use the API by sending an HTTP request to the API endpoints for example:
|
||||
```
|
||||
http://localhost:3040/v1/chat/completions
|
||||
```
|
||||
|
||||
```txt
|
||||
http://localhost:3000/v1/completions
|
||||
http://localhost:3000/v1/chat/completions
|
||||
```
|
||||
Note that the base url will be `http://localhost:3040/v1`
|
||||
|
||||
# Use Our Hosted API Reverse Proxy
|
||||
You can now use this address to connect to your self-hosted ChatGPT API Reverse Proxy from Android applications/websites that support reverse proxy configurations, on the same device.
|
||||
|
||||
To use our hosted ChatGPT API, you can use the following steps:
|
||||
## Accessing Our Hosted API
|
||||
|
||||
1. Join our [Discord](https://discord.pawan.krd) server.
|
||||
2. Get your API key from the `#Bot` channel by sending `/key` command.
|
||||
3. Use the API Key in your requests to the following endpoints.
|
||||
Utilize our pre-hosted ChatGPT-like API for free by:
|
||||
|
||||
## Text Completion:
|
||||
1. Joining our [Discord server](https://discord.pawan.krd).
|
||||
2. Obtaining an API key from the `#Bot` channel with the `/key` command.
|
||||
3. Incorporating the API key into your requests to:
|
||||
```
|
||||
https://api.pawan.krd/v1/chat/completions
|
||||
```
|
||||
|
||||
```txt
|
||||
https://api.pawan.krd/v1/completions
|
||||
```
|
||||
## Usage Examples
|
||||
|
||||
### Example: [OpenAI Docs](https://platform.openai.com/docs/api-reference/completions)
|
||||
Leverage the same integration code as OpenAI's official libraries by simply adjusting the API key and base URL in your requests. For self-hosted setups, ensure to switch the base URL to your local server's address as mentioned above.
|
||||
|
||||
```bash
|
||||
curl --location 'https://api.pawan.krd/v1/completions' \
|
||||
--header 'Authorization: Bearer pk-***[OUR_API_KEY]***' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "text-davinci-003",
|
||||
"prompt": "Human: Hello\\nAI:",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 256,
|
||||
"stop": [
|
||||
"Human:",
|
||||
"AI:"
|
||||
]
|
||||
}'
|
||||
```
|
||||
### Example Usage with OpenAI Libraries
|
||||
|
||||
## Chat Completion (ChatGPT):
|
||||
|
||||
```txt
|
||||
https://api.pawan.krd/v1/chat/completions
|
||||
```
|
||||
|
||||
### Example: [OpenAI Docs](https://platform.openai.com/docs/api-reference/chat)
|
||||
|
||||
```bash
|
||||
curl --location 'https://api.pawan.krd/v1/chat/completions' \
|
||||
--header 'Authorization: Bearer pk-***[OUR_API_KEY]***' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"max_tokens": 100,
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are an helpful assistant."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Who are you?"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Image Generation (DALL-E):
|
||||
|
||||
```txt
|
||||
https://api.pawan.krd/v1/images/generations
|
||||
```
|
||||
|
||||
### Example: [OpenAI Docs](https://platform.openai.com/docs/api-reference/images)
|
||||
|
||||
```bash
|
||||
curl --location 'https://api.pawan.krd/v1/images/generations' \
|
||||
--header 'Authorization: Bearer pk-***[OUR_API_KEY]***' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"prompt": "a photo of a happy corgi puppy sitting and facing forward, studio light, longshot.",
|
||||
"n": 1,
|
||||
"size": "1024x1024"
|
||||
}'
|
||||
```
|
||||
|
||||
## Examples using OpenAI libraries
|
||||
|
||||
You can use the same code to access the API using the official OpenAI libraries, the only difference is that you need to change the API key and the API base URL.
|
||||
|
||||
Examples are for text completion, but you can use the same code for chat completion and image generation.
|
||||
|
||||
### Python
|
||||
|
||||
You need to add the following lines before your code to use the API:
|
||||
#### Python Example
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
openai.api_key = 'pk-**********************************************'
|
||||
openai.api_base = 'https://api.pawan.krd/v1'
|
||||
```
|
||||
openai.api_key = 'pk-**********************************************' # For self-hosted version you can leave it empty
|
||||
openai.base_url = "https://api.pawan.krd/v1" # For self-hosted version, use "http://localhost:3040/v1"
|
||||
|
||||
Example code:
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
openai.api_key = 'pk-**********************************************'
|
||||
openai.api_base = 'https://api.pawan.krd/v1'
|
||||
|
||||
response = openai.Completion.create(
|
||||
model="text-davinci-003",
|
||||
prompt="Human: Hello\nAI:",
|
||||
temperature=0.7,
|
||||
max_tokens=256,
|
||||
top_p=1,
|
||||
frequency_penalty=0,
|
||||
presence_penalty=0,
|
||||
stop=["Human: ", "AI: "]
|
||||
completion = openai.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "How do I list all files in a directory using Python?"},
|
||||
],
|
||||
)
|
||||
|
||||
print(response.choices[0].text)
|
||||
print(completion.choices[0].message.content)
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
You need to add the following lines before your code to use the API:
|
||||
#### Node.js Example
|
||||
|
||||
```js
|
||||
import { Configuration, OpenAIApi } from "openai";
|
||||
|
||||
const configuration = new Configuration({
|
||||
apiKey: "pk-**********************************************",
|
||||
basePath: "https://api.pawan.krd/v1",
|
||||
});
|
||||
```
|
||||
|
||||
Example code:
|
||||
|
||||
```js
|
||||
import { Configuration, OpenAIApi } from "openai";
|
||||
|
||||
const configuration = new Configuration({
|
||||
apiKey: "pk-**********************************************",
|
||||
basePath: "https://api.pawan.krd/v1",
|
||||
apiKey: "pk-**********************************************", // For self-hosted version you can leave it empty
|
||||
basePath: "https://api.pawan.krd/v1", // For self-hosted version, use "http://localhost:3040/v1"
|
||||
});
|
||||
|
||||
const openai = new OpenAIApi(configuration);
|
||||
|
||||
const response = await openai.createCompletion({
|
||||
model: "text-davinci-003",
|
||||
prompt: "Human: Hello\nAI:",
|
||||
temperature: 0.7,
|
||||
max_tokens: 256,
|
||||
top_p: 1,
|
||||
frequency_penalty: 0,
|
||||
presence_penalty: 0,
|
||||
stop: ["Human: ", "AI: "],
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: "user", content: "Initiate a test message" }],
|
||||
model: "gpt-3.5-turbo",
|
||||
});
|
||||
|
||||
console.log(response.data.choices[0].text);
|
||||
console.log(chatCompletion.choices[0].message.content);
|
||||
```
|
||||
|
||||
# License
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
This project is under the AGPL-3.0 License. Refer to the [LICENSE](LICENSE) file for detailed information.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// Server configuration
|
||||
export const SERVER_PORT = 3000; // Server port
|
||||
export const DEBUG = false; // Debug mode
|
||||
|
||||
// Prompt Moderation before sending to OpenAI
|
||||
export const MODERATION = true; // Moderation mode
|
||||
|
||||
// Rate limit
|
||||
export const PRIOD = 15 * 1000; // 15 seconds
|
||||
export const RATE_LIMIT = 50; // 50 requests per 15 seconds
|
||||
|
||||
// Whitelisted IPs
|
||||
export const WHITELISTED_IPS = [
|
||||
// "127.0.0.1"
|
||||
];
|
||||
|
||||
// OpenAI API Keys
|
||||
export let OPENAI_KEYS = [
|
||||
"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
];
|
||||
@@ -1,45 +0,0 @@
|
||||
import { OPENAI_KEYS } from "./config.js";
|
||||
|
||||
async function* chunksToLines(chunksAsync) {
|
||||
let previous = "";
|
||||
for await (const chunk of chunksAsync) {
|
||||
const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
||||
previous += bufferChunk;
|
||||
let eolIndex;
|
||||
while ((eolIndex = previous.indexOf("\n")) >= 0) {
|
||||
// line includes the EOL
|
||||
const line = previous.slice(0, eolIndex + 1).trimEnd();
|
||||
if (line === "data: [DONE]") break;
|
||||
if (line.startsWith("data: ")) yield line;
|
||||
previous = previous.slice(eolIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function* linesToMessages(linesAsync) {
|
||||
for await (const line of linesAsync) {
|
||||
const message = line.substring("data :".length);
|
||||
|
||||
yield message;
|
||||
}
|
||||
}
|
||||
|
||||
async function* streamCompletion(data) {
|
||||
yield* linesToMessages(chunksToLines(data));
|
||||
}
|
||||
|
||||
function generateId() {
|
||||
const chars =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
let id = "org-";
|
||||
for (let i = 0; i < 24; i++) {
|
||||
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function getOpenAIKey() {
|
||||
return OPENAI_KEYS[Math.floor(Math.random() * OPENAI_KEYS.length)];
|
||||
}
|
||||
|
||||
export { generateId, getOpenAIKey, streamCompletion }
|
||||
@@ -1,33 +0,0 @@
|
||||
import express, { json, urlencoded } from 'express';
|
||||
import { completions, chatCompletions } from './routes.js';
|
||||
import { corsMiddleware, rateLimitMiddleware } from './middlewares.js';
|
||||
import { DEBUG, SERVER_PORT } from './config.js';
|
||||
|
||||
let app = express();
|
||||
|
||||
process.on("uncaughtException", function (err) {
|
||||
if (DEBUG) console.error(`Caught exception: ${err}`);
|
||||
});
|
||||
|
||||
// Middlewares
|
||||
app.use(corsMiddleware);
|
||||
app.use(rateLimitMiddleware);
|
||||
app.use(json());
|
||||
app.use(urlencoded({ extended: true }));
|
||||
|
||||
// Register routes
|
||||
app.all("/", async function (req, res) {
|
||||
res.set("Content-Type", "application/json");
|
||||
return res.status(200).send({
|
||||
status: true,
|
||||
github: "https://github.com/PawanOsman/ChatGPT",
|
||||
discord: "https://discord.pawan.krd"
|
||||
});
|
||||
});
|
||||
app.post("/v1/completions", completions);
|
||||
app.post("/v1/chat/completions", chatCompletions);
|
||||
|
||||
// Start server
|
||||
app.listen(SERVER_PORT, () => {
|
||||
console.log(`Listening on ${SERVER_PORT} ...`);
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { RATE_LIMIT, PRIOD, WHITELISTED_IPS } from "./config.js";
|
||||
|
||||
const rateLimit = new Map();
|
||||
|
||||
function corsMiddleware(req, res, next) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "*");
|
||||
next();
|
||||
};
|
||||
|
||||
async function rateLimitMiddleware(req, res, next) {
|
||||
let ip = req.headers["CF-Connecting-IP"] ?? req.headers["cf-connecting-ip"] ?? req.headers["X-Forwarded-For"] ?? req.headers["x-forwarded-for"] ?? req.ip;
|
||||
if (WHITELISTED_IPS.includes(ip)) return next();
|
||||
if (!rateLimit.has(ip)) {
|
||||
rateLimit.set(ip, {
|
||||
requests: 1,
|
||||
lastRequestTime: Date.now()
|
||||
});
|
||||
} else {
|
||||
const currentTime = Date.now();
|
||||
const timeSinceLastRequest = currentTime - rateLimit.get(ip).lastRequestTime;
|
||||
if (timeSinceLastRequest > PRIOD) {
|
||||
rateLimit.set(ip, {
|
||||
requests: 1,
|
||||
lastRequestTime: currentTime
|
||||
});
|
||||
} else {
|
||||
let updatedCount = rateLimit.get(ip).requests + 1;
|
||||
if (updatedCount > RATE_LIMIT) {
|
||||
return res.status(429).send({
|
||||
status: false,
|
||||
error: "Too many requests, please try again later"
|
||||
});
|
||||
}
|
||||
rateLimit.set(ip, {
|
||||
requests: updatedCount,
|
||||
lastRequestTime: rateLimit.get(ip).lastRequestTime
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
export { corsMiddleware, rateLimitMiddleware }
|
||||
Generated
+446
-566
File diff suppressed because it is too large
Load Diff
+17
-15
@@ -1,26 +1,28 @@
|
||||
{
|
||||
"name": "chatgpt",
|
||||
"version": "1.0.0",
|
||||
"description": "## If you have any questions or need assistance, please join [[Discord](https://discord.pawan.krd)]",
|
||||
"description": "OpenAI API Free Reverse Proxy",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"start": "tsc && node dist/app.js",
|
||||
"watch": "tsc-watch --onSuccess \"node dist/app.js\"",
|
||||
"build": "tsc"
|
||||
},
|
||||
"author": "Pawan Osman",
|
||||
"license": "AGPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/PawanOsman/ChatGPT.git"
|
||||
"url": "https://github.com/PawanOsman/ChatGPT.git"
|
||||
},
|
||||
"author": "PawanOsman <contact@pawan.krd> (https://pawan.krd/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/PawanOsman/ChatGPT/issues"
|
||||
},
|
||||
"homepage": "https://github.com/PawanOsman/ChatGPT#readme",
|
||||
"dependencies": {
|
||||
"axios": "1.3.4",
|
||||
"express": "4.18.2",
|
||||
"openai": "3.2.1"
|
||||
"axios": "^1.6.7",
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { Configuration, OpenAIApi } from "openai";
|
||||
import { streamCompletion, generateId, getOpenAIKey } from "./functions.js"
|
||||
import { DEBUG, MODERATION } from "./config.js";
|
||||
|
||||
async function completions(req, res) {
|
||||
let orgId = generateId();
|
||||
let key = getOpenAIKey();
|
||||
|
||||
if (!req.body.prompt) {
|
||||
res.set("Content-Type", "application/json");
|
||||
return res.status(400).send({
|
||||
status: false,
|
||||
error: "No prompt provided"
|
||||
});
|
||||
}
|
||||
|
||||
if (DEBUG) console.log(`[Text] [MAX-TOKENS:${req.body.max_tokens ?? "unset"}] ${req.body.prompt}`);
|
||||
|
||||
if (MODERATION) {
|
||||
try {
|
||||
let openAi = new OpenAIApi(new Configuration({ apiKey: key }));
|
||||
let response = await openAi.createModeration({
|
||||
input: req.body.prompt,
|
||||
});
|
||||
|
||||
if (response.data.results[0].flagged) {
|
||||
res.set("Content-Type", "application/json");
|
||||
return res.status(400).send({
|
||||
status: false,
|
||||
error: "Your prompt contains content that is not allowed",
|
||||
reason: response.data.results[0].reason,
|
||||
contact: "https://discord.pawan.krd"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.stream) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`https://api.openai.com/v1/completions`, req.body,
|
||||
{
|
||||
responseType: "stream",
|
||||
headers: {
|
||||
Accept: "text/event-stream",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
res.setHeader("content-type", "text/event-stream");
|
||||
|
||||
for await (const message of streamCompletion(response.data)) {
|
||||
try {
|
||||
const parsed = JSON.parse(message);
|
||||
delete parsed.id;
|
||||
delete parsed.created;
|
||||
res.write(`data: ${JSON.stringify(parsed)}\n\n`);
|
||||
} catch (error) {
|
||||
if (DEBUG) console.error("Could not JSON parse stream message", message, error);
|
||||
}
|
||||
}
|
||||
|
||||
res.write(`data: [DONE]`);
|
||||
res.end();
|
||||
} catch (error) {
|
||||
try {
|
||||
if (error.response && error.response.data) {
|
||||
let errorResponseStr = "";
|
||||
|
||||
for await (const message of error.response.data) {
|
||||
errorResponseStr += message;
|
||||
}
|
||||
|
||||
errorResponseStr = errorResponseStr.replace(/org-[a-zA-Z0-9]+/, orgId);
|
||||
|
||||
const errorResponseJson = JSON.parse(errorResponseStr);
|
||||
return res.status(error.response.status).send(errorResponseJson);
|
||||
} else {
|
||||
if (DEBUG) console.error("Could not JSON parse stream message", error);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`https://api.openai.com/v1/completions`, req.body,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
delete response.data.id;
|
||||
delete response.data.created;
|
||||
|
||||
return res.status(200).send(response.data);
|
||||
} catch (error) {
|
||||
try {
|
||||
error.response.data.error.message = error.response.data.error.message.replace(/org-[a-zA-Z0-9]+/, orgId);
|
||||
return res.status(error.response.status).send(error.response.data);
|
||||
}
|
||||
catch (e) {
|
||||
if (DEBUG) console.log(e);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function chatCompletions(req, res) {
|
||||
let orgId = generateId();
|
||||
let key = getOpenAIKey();
|
||||
|
||||
if (MODERATION) {
|
||||
try {
|
||||
let prompt = [];
|
||||
try {
|
||||
req.body.messages.forEach(element => {
|
||||
prompt.push(element.content);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
return res.status(400).send({
|
||||
status: false,
|
||||
error: "messages is required! and must be an array of objects with content and author properties"
|
||||
});
|
||||
}
|
||||
|
||||
if (DEBUG) console.log(`[CHAT] [MAX-TOKENS:${req.body.max_tokens ?? "unset"}] ${prompt}`);
|
||||
|
||||
let openAi = new OpenAIApi(new Configuration({ apiKey: key }));
|
||||
let response = await openAi.createModeration({
|
||||
input: prompt,
|
||||
});
|
||||
|
||||
if (response.data.results[0].flagged) {
|
||||
res.set("Content-Type", "application/json");
|
||||
return res.status(400).send({
|
||||
status: false,
|
||||
error: "Your prompt contains content that is not allowed",
|
||||
reason: response.data.results[0].reason,
|
||||
support: "https://discord.pawan.krd"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (DEBUG) console.log(e);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (DEBUG) console.log(`[CHAT] [MAX-TOKENS:${req.body.max_tokens ?? "unset"}]`);
|
||||
}
|
||||
|
||||
if (req.body.stream) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`https://api.openai.com/v1/chat/completions`, req.body,
|
||||
{
|
||||
responseType: "stream",
|
||||
headers: {
|
||||
Accept: "text/event-stream",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
res.setHeader("content-type", "text/event-stream");
|
||||
|
||||
for await (const message of streamCompletion(response.data)) {
|
||||
try {
|
||||
const parsed = JSON.parse(message);
|
||||
delete parsed.id;
|
||||
delete parsed.created;
|
||||
const { content } = parsed.choices[0].delta;
|
||||
if (content) {
|
||||
res.write(`data: ${JSON.stringify(parsed)}\n\n`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (DEBUG) console.error("Could not JSON parse stream message", message, error);
|
||||
}
|
||||
}
|
||||
|
||||
res.write(`data: [DONE]`);
|
||||
res.end();
|
||||
} catch (error) {
|
||||
try {
|
||||
if (error.response && error.response.data) {
|
||||
let errorResponseStr = "";
|
||||
|
||||
for await (const message of error.response.data) {
|
||||
errorResponseStr += message;
|
||||
}
|
||||
|
||||
errorResponseStr = errorResponseStr.replace(/org-[a-zA-Z0-9]+/, orgId);
|
||||
|
||||
const errorResponseJson = JSON.parse(errorResponseStr);
|
||||
return res.status(error.response.status).send(errorResponseJson);
|
||||
} else {
|
||||
if (DEBUG) console.error("Could not JSON parse stream message", error);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (DEBUG) console.log(e);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`https://api.openai.com/v1/chat/completions`, req.body,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
delete response.data.id;
|
||||
delete response.data.created;
|
||||
|
||||
return res.status(200).send(response.data);
|
||||
} catch (error) {
|
||||
try {
|
||||
error.response.data.error.message = error.response.data.error.message.replace(/org-[a-zA-Z0-9]+/, orgId);
|
||||
return res.status(error.response.status).send(error.response.data);
|
||||
}
|
||||
catch (e) {
|
||||
if (DEBUG) console.log(e);
|
||||
return res.status(500).send({
|
||||
status: false,
|
||||
error: "something went wrong!"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { completions, chatCompletions };
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
import express, { Request, Response, NextFunction } from "express";
|
||||
import bodyParser from "body-parser";
|
||||
import axios from "axios";
|
||||
import https from "https";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
// Constants for the server and API configuration
|
||||
const port = 3040;
|
||||
const baseUrl = "https://chat.openai.com";
|
||||
const apiUrl = `${baseUrl}/backend-api/conversation`;
|
||||
const refreshInterval = 60000; // Interval to refresh token in ms
|
||||
const errorWait = 120000; // Wait time in ms after an error
|
||||
|
||||
// Initialize global variables to store the session token and device ID
|
||||
let token: string;
|
||||
let oaiDeviceId: string;
|
||||
|
||||
// Function to wait for a specified duration
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
function GenerateCompletionId(prefix: string = "cmpl-") {
|
||||
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
const length = 28;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
prefix += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
async function* chunksToLines(chunksAsync: any) {
|
||||
let previous = "";
|
||||
for await (const chunk of chunksAsync) {
|
||||
const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
||||
previous += bufferChunk;
|
||||
let eolIndex: number;
|
||||
while ((eolIndex = previous.indexOf("\n")) >= 0) {
|
||||
// line includes the EOL
|
||||
const line = previous.slice(0, eolIndex + 1).trimEnd();
|
||||
if (line === "data: [DONE]") break;
|
||||
if (line.startsWith("data: ")) yield line;
|
||||
previous = previous.slice(eolIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function* linesToMessages(linesAsync: any) {
|
||||
for await (const line of linesAsync) {
|
||||
const message = line.substring("data :".length);
|
||||
|
||||
yield message;
|
||||
}
|
||||
}
|
||||
|
||||
async function* StreamCompletion(data: any) {
|
||||
yield* linesToMessages(chunksToLines(data));
|
||||
}
|
||||
|
||||
// Setup axios instance for API requests with predefined configurations
|
||||
const axiosInstance = axios.create({
|
||||
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
|
||||
headers: {
|
||||
accept: "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"cache-control": "no-cache",
|
||||
"content-type": "application/json",
|
||||
"oai-language": "en-US",
|
||||
origin: baseUrl,
|
||||
pragma: "no-cache",
|
||||
referer: baseUrl,
|
||||
"sec-ch-ua": '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Windows"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
},
|
||||
});
|
||||
|
||||
// Function to get a new session ID and token from the OpenAI API
|
||||
async function getNewSessionId() {
|
||||
let newDeviceId = randomUUID();
|
||||
const response = await axiosInstance.post(
|
||||
`${baseUrl}/backend-anon/sentinel/chat-requirements`,
|
||||
{},
|
||||
{
|
||||
headers: { "oai-device-id": newDeviceId },
|
||||
}
|
||||
);
|
||||
console.log(`System: Successfully refreshed session ID and token. ${!token ? "(Now it's ready to process requests)" : ""}`);
|
||||
oaiDeviceId = newDeviceId;
|
||||
token = response.data.token;
|
||||
|
||||
// console.log("New Token:", token);
|
||||
// console.log("New Device ID:", oaiDeviceId);
|
||||
}
|
||||
|
||||
// Middleware to enable CORS and handle pre-flight requests
|
||||
function enableCORS(req: Request, res: Response, next: NextFunction) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||
if (req.method === "OPTIONS") {
|
||||
return res.status(200).end();
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// Middleware to handle chat completions
|
||||
async function handleChatCompletion(req: Request, res: Response) {
|
||||
console.log("Request:", `${req.method} ${req.originalUrl}`, `${req.body?.messages?.length ?? 0} messages`, req.body.stream ? "(stream-enabled)" : "(stream-disabled)");
|
||||
try {
|
||||
const body = {
|
||||
action: "next",
|
||||
messages: req.body.messages.map((message) => ({
|
||||
author: { role: message.role },
|
||||
content: { content_type: "text", parts: [message.content] },
|
||||
})),
|
||||
parent_message_id: randomUUID(),
|
||||
model: "text-davinci-002-render-sha",
|
||||
timezone_offset_min: -180,
|
||||
suggestions: [],
|
||||
history_and_training_disabled: true,
|
||||
conversation_mode: { kind: "primary_assistant" },
|
||||
websocket_request_id: randomUUID(),
|
||||
};
|
||||
|
||||
const response = await axiosInstance.post(apiUrl, body, {
|
||||
responseType: "stream",
|
||||
headers: {
|
||||
"oai-device-id": oaiDeviceId,
|
||||
"openai-sentinel-chat-requirements-token": token,
|
||||
},
|
||||
});
|
||||
|
||||
// Set the response headers based on the request type
|
||||
if (req.body.stream) {
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
} else {
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
let fullContent = "";
|
||||
let requestId = GenerateCompletionId("chatcmpl-");
|
||||
let created = Date.now();
|
||||
|
||||
for await (const message of StreamCompletion(response.data)) {
|
||||
const parsed = JSON.parse(message);
|
||||
|
||||
let content = parsed?.message?.content?.parts[0] ?? "";
|
||||
|
||||
for (let message of req.body.messages) {
|
||||
if (message.content === content) {
|
||||
content = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (content === "") continue;
|
||||
|
||||
if (req.body.stream) {
|
||||
let response = {
|
||||
id: requestId,
|
||||
created: created,
|
||||
object: "chat.completion.chunk",
|
||||
model: "gpt-3.5-turbo",
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
content: content.replace(fullContent, ""),
|
||||
},
|
||||
index: 0,
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
res.write(`data: ${JSON.stringify(response)}\n\n`);
|
||||
}
|
||||
|
||||
fullContent = content.length > fullContent.length ? content : fullContent;
|
||||
}
|
||||
|
||||
if (req.body.stream) {
|
||||
res.write(
|
||||
`data: ${JSON.stringify({
|
||||
id: requestId,
|
||||
created: created,
|
||||
object: "chat.completion.chunk",
|
||||
model: "gpt-3.5-turbo",
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
content: "",
|
||||
},
|
||||
index: 0,
|
||||
finish_reason: "stop",
|
||||
},
|
||||
],
|
||||
})}\n\n`
|
||||
);
|
||||
} else {
|
||||
res.write(
|
||||
JSON.stringify({
|
||||
id: requestId,
|
||||
created: created,
|
||||
model: "gpt-3.5-turbo",
|
||||
object: "chat.completion",
|
||||
choices: [
|
||||
{
|
||||
finish_reason: "stop",
|
||||
index: 0,
|
||||
message: {
|
||||
content: fullContent,
|
||||
role: "assistant",
|
||||
},
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
res.end();
|
||||
} catch (error: any) {
|
||||
// console.log('Error:', error.response?.data ?? error.message);
|
||||
if (!res.headersSent) res.setHeader("Content-Type", "application/json");
|
||||
// console.error('Error handling chat completion:', error);
|
||||
res.write(
|
||||
JSON.stringify({
|
||||
status: false,
|
||||
error: {
|
||||
message: "An error happened, please make sure your request is SFW, or use a jailbreak to bypass the filter.",
|
||||
type: "invalid_request_error",
|
||||
},
|
||||
support: "https://discord.pawan.krd",
|
||||
})
|
||||
);
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Express app and use middlewares
|
||||
const app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.use(enableCORS);
|
||||
|
||||
// Route to handle POST requests for chat completions
|
||||
app.post("/v1/chat/completions", handleChatCompletion);
|
||||
|
||||
// 404 handler for unmatched routes
|
||||
app.use((req, res) =>
|
||||
res.status(404).send({
|
||||
status: false,
|
||||
error: {
|
||||
message: `The requested endpoint was not found. please make sure to use "http://localhost:3040/v1" as the base URL.`,
|
||||
type: "invalid_request_error",
|
||||
},
|
||||
support: "https://discord.pawan.krd",
|
||||
})
|
||||
);
|
||||
|
||||
// Start the server and the session ID refresh loop
|
||||
app.listen(port, () => {
|
||||
console.log(`💡 Server is running at http://localhost:${port}`);
|
||||
console.log();
|
||||
console.log(`🔗 Base URL: http://localhost:${port}/v1`);
|
||||
console.log(`🔗 ChatCompletion Endpoint: http://localhost:${port}/v1/chat/completions`);
|
||||
console.log();
|
||||
console.log("📝 Author: Pawan.Krd");
|
||||
console.log(`🌐 Discord server: https://discord.gg/pawan`);
|
||||
console.log("🌍 GitHub Repository: https://github.com/PawanOsman/ChatGPT");
|
||||
console.log(`Don't forget to ⭐ star the repository if you like this project!`);
|
||||
console.log();
|
||||
|
||||
setTimeout(async () => {
|
||||
while (true) {
|
||||
try {
|
||||
await getNewSessionId();
|
||||
await wait(refreshInterval);
|
||||
} catch (error) {
|
||||
console.error("Error refreshing session ID, retrying in 1 minute...");
|
||||
console.error("If this error persists, your country may not be supported yet.");
|
||||
console.error("If your country was the issue, please consider using a U.S. VPN.");
|
||||
await wait(errorWait);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
|
||||
IF NOT EXIST node_modules (
|
||||
echo Installing npm packages...
|
||||
call npm install
|
||||
)
|
||||
|
||||
cls
|
||||
echo Starting the application...
|
||||
call npm start
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "Installing npm packages..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
clear
|
||||
echo "Starting the application..."
|
||||
npm start
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"module": "ESNext",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"incremental": true,
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user