From 69cef746eee9082f141768081d31470e0b61d093 Mon Sep 17 00:00:00 2001
From: LanQian <5499636+LanQian528@users.noreply.github.com>
Date: Mon, 27 May 2024 15:48:07 +0800
Subject: [PATCH] v1.1.12 add auth_key, fix bugs
---
.github/workflows/build_docker.yml | 2 +-
README.md | 35 ++++++++++-----------
chat2api.py | 3 +-
chatgpt/ChatService.py | 49 ++++++++++++++++++------------
chatgpt/chatFormat.py | 4 ++-
chatgpt/chatLimit.py | 4 ++-
chatgpt/refreshToken.py | 4 +--
chatgpt/reverseProxy.py | 7 +++--
chatgpt/wssClient.py | 2 +-
templates/tokens.html | 33 +++++++++++---------
utils/config.py | 4 ++-
11 files changed, 86 insertions(+), 61 deletions(-)
diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml
index db3dbb7..813f9a9 100644
--- a/.github/workflows/build_docker.yml
+++ b/.github/workflows/build_docker.yml
@@ -37,7 +37,7 @@ jobs:
images: lanqian528/chat2api
tags: |
type=raw,value=latest,enable={{is_default_branch}}
- type=raw,value=v1.1.11
+ type=raw,value=v1.1.12
- name: Build and push
uses: docker/build-push-action@v5
diff --git a/README.md b/README.md
index c35168c..9e18ce3 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,8 @@
3. 接口返回的状态码和响应体
## 功能
-### 最新版 v1.1.11
+
+### 最新版 v1.1.12
> 已完成
> - [x] 流式、非流式传输
@@ -55,22 +56,22 @@
每个环境变量都有默认值,如果不懂环境变量的含义,请不要设置,更不要传空值,字符串无需引号。
-| 分类 | 变量名 | 示例值 | 描述 |
-|------|-------------------|-------------------------------------|--------------------------------------------------------------|
-| 安全相关 | API_PREFIX | your_prefix | API 前缀密码,不设置容易被人访问,设置后需请求 `/your_prefix/v1/chat/completions` |
-| | AUTHORIZATION | sk-xxxxxxxx, sk-yyyyyyyy | 为使用多账号轮询 Tokens 设置的授权,英文逗号分隔 |
-| 请求相关 | CHATGPT_BASE_URL | https://chatgpt.com | ChatGPT 网关地址,设置后会改变请求的网站,多个网关用逗号分隔 |
-| | PROXY_URL | your_first_proxy, your_second_proxy | 代理 URL,多个代理用逗号分隔 |
-| | ARKOSE_TOKEN_URL | https://arkose.example.com/token | 获取 Arkose token 的地址 |
-| 功能相关 | HISTORY_DISABLED | true | 是否不保存聊天记录并返回 conversation_id |
-| | POW_DIFFICULTY | 00003a | 要解决的工作量证明难度,不懂别设置 |
-| | RETRY_TIMES | 3 | 出错重试次数,使用 AUTHORIZATION 会自动轮询下一个账号 |
-| | ENABLE_GATEWAY | true | 是否启用网关模式(WEBUI) |
-| | CONVERSATION_ONLY | false | 是否直接使用对话接口,如果你用的网关支持自动解决pow和arkose才启用 |
-| | ENABLE_LIMIT | true | 开启后不尝试突破官方次数限制,尽可能防止封号 |
-| | UPLOAD_BY_URL | false | 开启后按照 `URL+空格+正文` 进行对话,自动解析 URL 内容并上传,多个 URL 用空格分隔 |
-| | CHECK_MODEL | false | 检查账号是否支持传入模型,开启后可以稍微避免4o返回3.5内容,但是会增加请求时延,且并不能解决降智问题 |
-
+| 分类 | 变量名 | 示例值 | 默认值 | 描述 |
+|------|-------------------|-------------------------------------------------------------|-----------------------|--------------------------------------------------------------|
+| 安全相关 | API_PREFIX | `your_prefix` | `None` | API 前缀密码,不设置容易被人访问,设置后需请求 `/your_prefix/v1/chat/completions` |
+| | AUTHORIZATION | `your_first_authorization`,
`your_second_authorization` | `[]` | 你自己为使用多账号轮询 Tokens 设置的授权,英文逗号分隔 |
+| | AUTH_KEY | `your_auth_key` | `None` | 私人网关需要加`auth_key`请求头才设置该项 |
+| 请求相关 | CHATGPT_BASE_URL | `https://chatgpt.com` | `https://chatgpt.com` | ChatGPT 网关地址,设置后会改变请求的网站,多个网关用逗号分隔 |
+| | PROXY_URL | `http://ip:port`,
`http://username:password@ip:port` | `[]` | 代理 URL,多个代理用逗号分隔 |
+| | ARKOSE_TOKEN_URL | `https://example.com/token` | `[]` | 获取 Arkose token 的地址 |
+| 功能相关 | HISTORY_DISABLED | `true` | `true` | 是否不保存聊天记录并返回 conversation_id |
+| | POW_DIFFICULTY | `00003a` | `00003a` | 要解决的工作量证明难度,不懂别设置 |
+| | RETRY_TIMES | `3` | `3` | 出错重试次数,使用 AUTHORIZATION 会自动轮询下一个账号 |
+| | ENABLE_GATEWAY | `true` | `true` | 是否启用网关模式(WEBUI) |
+| | CONVERSATION_ONLY | `false` | `false` | 是否直接使用对话接口,如果你用的网关支持自动解决pow和arkose才启用 |
+| | ENABLE_LIMIT | `true` | `true` | 开启后不尝试突破官方次数限制,尽可能防止封号 |
+| | UPLOAD_BY_URL | `false` | `false` | 开启后按照 `URL+空格+正文` 进行对话,自动解析 URL 内容并上传,多个 URL 用空格分隔 |
+| | CHECK_MODEL | `false` | `false` | 检查账号是否支持传入模型,开启后可以稍微避免4o返回3.5内容,但是会增加请求时延,且并不能解决降智问题 |
## 部署
diff --git a/chat2api.py b/chat2api.py
index 4ce2355..927b6b9 100644
--- a/chat2api.py
+++ b/chat2api.py
@@ -87,7 +87,8 @@ async def send_conversation(request: Request, req_token: str = Depends(oauth2_sc
@app.get(f"/{api_prefix}/tokens" if api_prefix else "/tokens", response_class=HTMLResponse)
async def upload_html(request: Request):
tokens_count = len(token_list)
- return templates.TemplateResponse("tokens.html", {"request": request, "api_prefix": api_prefix, "tokens_count": tokens_count})
+ return templates.TemplateResponse("tokens.html",
+ {"request": request, "api_prefix": api_prefix, "tokens_count": tokens_count})
@app.post(f"/{api_prefix}/tokens/upload" if api_prefix else "/tokens/upload")
diff --git a/chatgpt/ChatService.py b/chatgpt/ChatService.py
index 8c85865..5adc3e1 100644
--- a/chatgpt/ChatService.py
+++ b/chatgpt/ChatService.py
@@ -17,7 +17,7 @@ from utils.Client import Client
from utils.Logger import logger
from utils.authorization import verify_token
from utils.config import proxy_url_list, chatgpt_base_url_list, arkose_token_url_list, history_disabled, pow_difficulty, \
- conversation_only, enable_limit, limit_status_code, upload_by_url, check_model
+ conversation_only, enable_limit, limit_status_code, upload_by_url, check_model, auth_key
class ChatService:
@@ -61,9 +61,20 @@ class ChatService:
self.history_disabled = data.get('history_disabled', history_disabled)
self.data = data
+
self.origin_model = self.data.get("model", "gpt-3.5-turbo-0125")
self.resp_model = model_proxy.get(self.origin_model, self.origin_model)
- self.req_model = None
+ if "gpt-4o" in self.origin_model:
+ self.req_model = "gpt-4o"
+ elif "gpt-4-mobile" in self.origin_model:
+ self.req_model = "gpt-4-mobile"
+ elif "gpt-4-gizmo" in self.origin_model:
+ self.req_model = "gpt-4o"
+ elif "gpt-4" in self.origin_model:
+ self.req_model = "gpt-4"
+ else:
+ self.req_model = "text-davinci-002-render-sha"
+
self.api_messages = self.data.get("messages", [])
self.prompt_tokens = 0
self.max_tokens = self.data.get("max_tokens", 2147483647)
@@ -99,8 +110,12 @@ class ChatService:
else:
self.base_url = self.host_url + "/backend-anon"
+ if auth_key:
+ self.base_headers['authkey'] = auth_key
+
await get_dpl(self)
- self.s.session.cookies.set("__Secure-next-auth.callback-url", "https%3A%2F%2Fchatgpt.com;", domain=self.host_url.split("://")[1], secure=True)
+ self.s.session.cookies.set("__Secure-next-auth.callback-url", "https%3A%2F%2Fchatgpt.com;",
+ domain=self.host_url.split("://")[1], secure=True)
async def get_wss_url(self):
url = f'{self.base_url}/register-websocket'
@@ -129,17 +144,6 @@ class ChatService:
if r.status_code == 200:
resp = r.json()
- if "gpt-4o" in self.origin_model:
- self.req_model = "gpt-4o"
- elif "gpt-4-mobile" in self.origin_model:
- self.req_model = "gpt-4-mobile"
- elif "gpt-4-gizmo" in self.origin_model:
- self.req_model = "gpt-4o"
- elif "gpt-4" in self.origin_model:
- self.req_model = "gpt-4"
- else:
- self.req_model = "text-davinci-002-render-sha"
-
if check_model:
r = await self.s.get(f'{self.base_url}/models', headers=headers, timeout=5)
if r.status_code == 200:
@@ -174,9 +178,11 @@ class ChatService:
if proofofwork_required:
proofofwork_diff = proofofwork.get("difficulty")
if proofofwork_diff <= pow_difficulty:
- raise HTTPException(status_code=403, detail=f"Proof of work difficulty too high: {proofofwork_diff}")
+ raise HTTPException(status_code=403,
+ detail=f"Proof of work difficulty too high: {proofofwork_diff}")
proofofwork_seed = proofofwork.get("seed")
- self.proof_token, solved = await run_in_threadpool(get_answer_token, proofofwork_seed, proofofwork_diff, config)
+ self.proof_token, solved = await run_in_threadpool(get_answer_token, proofofwork_seed,
+ proofofwork_diff, config)
if not solved:
raise HTTPException(status_code=403, detail="Failed to solve proof of work")
@@ -285,7 +291,8 @@ class ChatService:
raise HTTPException(status_code=e.status_code, detail=str(e))
url = f'{self.base_url}/conversation'
stream = self.data.get("stream", False)
- r = await self.s.post_stream(url, headers=self.chat_headers, json=self.chat_request, timeout=10, stream=True)
+ r = await self.s.post_stream(url, headers=self.chat_headers, json=self.chat_request, timeout=10,
+ stream=True)
if r.status_code != 200:
rtext = await r.atext()
if "application/json" == r.headers.get("Content-Type", ""):
@@ -304,7 +311,9 @@ class ChatService:
if "text/event-stream" in content_type and stream:
return stream_response(self, r.aiter_lines(), self.resp_model, self.max_tokens)
elif "text/event-stream" in content_type and not stream:
- return await format_not_stream_response(stream_response(self, r.aiter_lines(), self.resp_model, self.max_tokens), self.prompt_tokens, self.max_tokens, self.resp_model)
+ return await format_not_stream_response(
+ stream_response(self, r.aiter_lines(), self.resp_model, self.max_tokens), self.prompt_tokens,
+ self.max_tokens, self.resp_model)
elif "application/json" in content_type:
rtext = await r.atext()
resp = json.loads(rtext)
@@ -319,7 +328,9 @@ class ChatService:
if stream and isinstance(wss_r, types.AsyncGeneratorType):
return stream_response(self, wss_r, self.resp_model, self.max_tokens)
else:
- return await format_not_stream_response(stream_response(self, wss_r, self.resp_model, self.max_tokens), self.prompt_tokens, self.max_tokens, self.resp_model)
+ return await format_not_stream_response(
+ stream_response(self, wss_r, self.resp_model, self.max_tokens), self.prompt_tokens,
+ self.max_tokens, self.resp_model)
finally:
if not isinstance(wss_r, types.AsyncGeneratorType):
await self.ws.close()
diff --git a/chatgpt/chatFormat.py b/chatgpt/chatFormat.py
index 5db68dc..9d95da6 100644
--- a/chatgpt/chatFormat.py
+++ b/chatgpt/chatFormat.py
@@ -248,7 +248,9 @@ async def stream_response(service, response, model, max_tokens):
def get_url_from_content(content):
if isinstance(content, str) and content.startswith('http'):
try:
- url = re.match(r'(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))', content.split(' ')[0])[0]
+ url = re.match(
+ r'(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))',
+ content.split(' ')[0])[0]
content = content.replace(url, '').strip()
return url, content
except Exception:
diff --git a/chatgpt/chatLimit.py b/chatgpt/chatLimit.py
index c444f6f..f446036 100644
--- a/chatgpt/chatLimit.py
+++ b/chatgpt/chatLimit.py
@@ -1,6 +1,7 @@
import threading
import time
from datetime import datetime
+
from utils.Logger import logger
lock = threading.Lock()
@@ -16,7 +17,8 @@ def check_isLimit(detail, access_token):
def initial_access_list(key, clear_time):
with lock:
limit_access_token[key] = clear_time
- logger.info(f"{key[:40]}: Reached 429 limit, will be cleared at {datetime.fromtimestamp(clear_time).replace(second=0, microsecond=0)}")
+ logger.info(
+ f"{key[:40]}: Reached 429 limit, will be cleared at {datetime.fromtimestamp(clear_time).replace(second=0, microsecond=0)}")
def remove_refresh_list(key):
diff --git a/chatgpt/refreshToken.py b/chatgpt/refreshToken.py
index 0485e70..945ccd5 100644
--- a/chatgpt/refreshToken.py
+++ b/chatgpt/refreshToken.py
@@ -9,7 +9,6 @@ from utils.Client import Client
from utils.Logger import logger
from utils.config import proxy_url_list
-
DATA_FOLDER = "data"
REFRESH_MAP_FILE = os.path.join(DATA_FOLDER, "refresh_map.json")
@@ -29,7 +28,8 @@ def save_refresh_map(refresh_map):
async def rt2ac(refresh_token):
- if refresh_token in refresh_map and int(time.time()) - refresh_map.get(refresh_token, {}).get("timestamp", 0) < 2 * 24 * 60 * 60:
+ if refresh_token in refresh_map and int(time.time()) - refresh_map.get(refresh_token, {}).get("timestamp",
+ 0) < 2 * 24 * 60 * 60:
access_token = refresh_map[refresh_token]["token"]
logger.info(f"refresh_token -> access_token from cache")
return access_token
diff --git a/chatgpt/reverseProxy.py b/chatgpt/reverseProxy.py
index 9de9faa..27c40b5 100644
--- a/chatgpt/reverseProxy.py
+++ b/chatgpt/reverseProxy.py
@@ -113,10 +113,11 @@ async def chatgpt_reverse_proxy(request: Request, path: str):
if "oai-dm=1" not in r.headers.get("Location"):
return Response(status_code=307, headers={
"Location": r.headers.get("Location").replace("chat.openai.com", origin_host)
- .replace("chatgpt.com", origin_host)
- .replace("https", petrol) + "?oai-dm=1"}, background=background)
+ .replace("chatgpt.com", origin_host)
+ .replace("https", petrol) + "?oai-dm=1"}, background=background)
else:
- return Response(status_code=307, headers={"Location": r.headers.get("Location")}, background=background)
+ return Response(status_code=307, headers={"Location": r.headers.get("Location")},
+ background=background)
elif r.status_code == 302:
return Response(status_code=302,
headers={"Location": r.headers.get("Location").replace("chatgpt.com", origin_host)
diff --git a/chatgpt/wssClient.py b/chatgpt/wssClient.py
index 3cf75b6..8d71eb8 100644
--- a/chatgpt/wssClient.py
+++ b/chatgpt/wssClient.py
@@ -20,4 +20,4 @@ async def ac2wss(access_token):
async def set_wss(access_token, wss_url):
wss_map[access_token] = {"timestamp": int(time.time()), "wss_url": wss_url}
- return True
\ No newline at end of file
+ return True
diff --git a/templates/tokens.html b/templates/tokens.html
index d4cb3c2..3e04416 100644
--- a/templates/tokens.html
+++ b/templates/tokens.html
@@ -2,7 +2,7 @@
当前可用 Tokens 数量:{{ tokens_count }}
- - -点击清空,将会清空所有已保存的 Tokens
-当前可用 Tokens 数量:{{ tokens_count }}
+ + +点击清空,将会清空所有已保存的 Tokens
+