Skip to content

Commit 72ad066

Browse files
committed
feat: support proxy; remove revChatGPT
1 parent fd990cd commit 72ad066

File tree

11 files changed

+120
-40
lines changed

11 files changed

+120
-40
lines changed

backend/api/conf/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class AuthSetting(BaseModel):
5353
class OpenaiWebChatGPTSetting(BaseModel):
5454
is_plus_account: bool = False
5555
chatgpt_base_url: Optional[str] = None
56+
proxy: Optional[str] = None
5657
common_timeout: int = Field(10, ge=1) # connect, read, write
5758
ask_timeout: int = Field(600, ge=1)
5859

@@ -65,6 +66,7 @@ def chatgpt_base_url_end_with_slash(cls, v):
6566

6667
class OpenaiApiSetting(BaseModel):
6768
openai_base_url: str = 'https://api.openai.com/v1/'
69+
proxy: Optional[str] = None
6870
connect_timeout: int = Field(10, ge=1)
6971
read_timeout: int = Field(20, ge=1)
7072

backend/api/conf/credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
class CredentialsModel(BaseModel):
12-
chatgpt_access_token: Optional[str] = None
12+
openai_web_access_token: Optional[str] = None
1313
# chatgpt_account_username: Optional[str] = None
1414
# chatgpt_account_password: Optional[str] = None
1515
openai_api_key: Optional[str] = None
@@ -18,7 +18,7 @@ class CredentialsModel(BaseModel):
1818
@singleton_with_lock
1919
class Credentials(BaseConfig[CredentialsModel]):
2020
if _TYPE_CHECKING:
21-
revchatgpt_access_token: Optional[str]
21+
openai_web_access_token: Optional[str]
2222
# chatgpt_account_username: Optional[str]
2323
# chatgpt_account_password: Optional[str]
2424
openai_api_key: Optional[str]

backend/api/routers/chat.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,6 @@ async def reply(response: AskResponse):
268268
# rev: 更改状态为 asking
269269
if ask_request.source == ChatSourceTypes.openai_web:
270270
await change_user_chat_status(user.id, OpenaiWebChatStatus.asking)
271-
openai_web_manager.reset_chat()
272271

273272
await reply(AskResponse(
274273
type=AskResponseType.waiting,
@@ -370,7 +369,6 @@ async def reply(response: AskResponse):
370369
if ask_request.source == ChatSourceTypes.openai_web:
371370
openai_web_manager.semaphore.release()
372371
await change_user_chat_status(user.id, OpenaiWebChatStatus.idling)
373-
openai_web_manager.reset_chat()
374372

375373
ask_stop_time = time.time()
376374
queueing_time = 0

backend/api/routers/system.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from api.models.doc import RequestLogDocument, AskLogDocument
1919
from api.schemas import LogFilterOptions, SystemInfo, UserCreate, UserSettingSchema, OpenaiWebSourceSettingSchema, \
2020
OpenaiApiSourceSettingSchema, RequestLogAggregation, AskLogAggregation
21+
from api.sources import OpenaiWebChatManager, OpenaiApiChatManager
2122
from api.users import current_super_user, get_user_manager_context
2223
from utils.logger import get_logger
2324

@@ -253,6 +254,10 @@ async def get_config(_user: User = Depends(current_super_user)):
253254
async def update_config(config_model: ConfigModel, _user: User = Depends(current_super_user)):
254255
config.update(config_model)
255256
config.save()
257+
openai_web_manager = OpenaiWebChatManager()
258+
openai_api_manager = OpenaiApiChatManager()
259+
openai_web_manager.reset_session()
260+
openai_api_manager.reset_session()
256261
return config.model()
257262

258263

@@ -266,6 +271,8 @@ async def get_credentials(_user: User = Depends(current_super_user)):
266271
async def update_credentials(credentials_model: CredentialsModel, _user: User = Depends(current_super_user)):
267272
credentials.update(credentials_model)
268273
credentials.save()
274+
openai_web_manager = OpenaiWebChatManager()
275+
openai_web_manager.reset_session()
269276
return credentials.model()
270277

271278

backend/api/sources/openai_api.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,29 @@ async def _check_response(response: httpx.Response) -> None:
4646
raise error from ex
4747

4848

49+
def make_session() -> httpx.AsyncClient:
50+
if config.openai_api.proxy is not None:
51+
proxies = {
52+
"http://": config.openai_api.proxy,
53+
"https://": config.openai_api.proxy,
54+
}
55+
session = httpx.AsyncClient(proxies=proxies, timeout=None)
56+
else:
57+
session = httpx.AsyncClient(timeout=None)
58+
return session
59+
60+
4961
@singleton_with_lock
5062
class OpenaiApiChatManager:
5163
"""
5264
OpenAI API Manager
5365
"""
5466

5567
def __init__(self):
56-
self.client = httpx.AsyncClient(timeout=None) # TODO: support proxies
68+
self.session = make_session()
69+
70+
def reset_session(self):
71+
self.session = make_session()
5772

5873
async def ask(self, content: str, conversation_id: uuid.UUID = None,
5974
parent_id: uuid.UUID = None, model: OpenaiApiChatModels = None,
@@ -126,7 +141,7 @@ async def ask(self, content: str, conversation_id: uuid.UUID = None,
126141

127142
timeout = httpx.Timeout(config.openai_api.read_timeout, connect=config.openai_api.connect_timeout)
128143

129-
async with self.client.stream(
144+
async with self.session.stream(
130145
method="POST",
131146
url=f"{base_url}chat/completions",
132147
json=data,

backend/api/sources/openai_web.py

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
from typing import AsyncGenerator
55

66
import httpx
7-
import revChatGPT
87
from fastapi.encoders import jsonable_encoder
98
from pydantic import parse_obj_as, ValidationError
10-
from revChatGPT.V1 import AsyncChatbot
119

1210
from api.conf import Config, Credentials
1311
from api.enums import OpenaiWebChatModels, ChatSourceTypes
@@ -128,32 +126,55 @@ async def _check_response(response: httpx.Response) -> None:
128126
raise error from ex
129127

130128

129+
def make_session() -> httpx.AsyncClient:
130+
if config.openai_web.proxy is not None:
131+
proxies = {
132+
"http://": config.openai_web.proxy,
133+
"https://": config.openai_web.proxy,
134+
}
135+
session = httpx.AsyncClient(proxies=proxies)
136+
else:
137+
session = httpx.AsyncClient()
138+
session.headers.clear()
139+
session.headers.update(
140+
{
141+
"Accept": "text/event-stream",
142+
"Authorization": f"Bearer {credentials.openai_web_access_token}",
143+
"Content-Type": "application/json",
144+
"X-Openai-Assistant-App-Id": "",
145+
"Connection": "close",
146+
"Accept-Language": "en-US,en;q=0.9",
147+
"Referer": "https://chat.openai.com/chat",
148+
},
149+
)
150+
return session
151+
152+
131153
@singleton_with_lock
132154
class OpenaiWebChatManager:
133155
"""
134156
TODO: 解除 revChatGPT 依赖
135157
"""
136158

137159
def __init__(self):
138-
self.chatbot = AsyncChatbot({
139-
"access_token": credentials.chatgpt_access_token,
140-
"paid": config.openai_web.is_plus_account,
141-
"model": "text-davinci-002-render-sha", # default model
142-
}, base_url=config.openai_web.chatgpt_base_url)
160+
self.session = make_session()
143161
self.semaphore = asyncio.Semaphore(1)
144162

145163
def is_busy(self):
146164
return self.semaphore.locked()
147165

166+
def reset_session(self):
167+
self.session = make_session()
168+
148169
async def get_conversations(self, timeout=None):
149170
all_conversations = []
150171
offset = 0
151172
limit = 80
152173
while True:
153-
url = f"{self.chatbot.base_url}conversations?offset={offset}&limit={limit}"
174+
url = f"{config.openai_web.chatgpt_base_url}conversations?offset={offset}&limit={limit}"
154175
if timeout is None:
155176
timeout = httpx.Timeout(config.openai_web.common_timeout)
156-
response = await self.chatbot.session.get(url, timeout=timeout)
177+
response = await self.session.get(url, timeout=timeout)
157178
await _check_response(response)
158179
data = json.loads(response.text)
159180
conversations = data["items"]
@@ -165,9 +186,8 @@ async def get_conversations(self, timeout=None):
165186
return all_conversations
166187

167188
async def get_conversation_history(self, conversation_id: uuid.UUID | str) -> OpenaiWebConversationHistoryDocument:
168-
# result = await self.chatbot.get_msg_history(conversation_id)
169-
url = f"{self.chatbot.base_url}conversation/{conversation_id}"
170-
response = await self.chatbot.session.get(url, timeout=None)
189+
url = f"{config.openai_web.chatgpt_base_url}conversation/{conversation_id}"
190+
response = await self.session.get(url, timeout=None)
171191
response.encoding = 'utf-8'
172192
await _check_response(response)
173193
result = json.loads(response.text)
@@ -198,7 +218,10 @@ async def get_conversation_history(self, conversation_id: uuid.UUID | str) -> Op
198218
return doc
199219

200220
async def clear_conversations(self):
201-
await self.chatbot.clear_conversations()
221+
# await self.chatbot.clear_conversations()
222+
url = f"{config.openai_web.chatgpt_base_url}conversations"
223+
response = await self.session.patch(url, data={"is_visible": False})
224+
await _check_response(response)
202225

203226
async def ask(self, content: str, conversation_id: uuid.UUID = None, parent_id: uuid.UUID = None,
204227
model: OpenaiWebChatModels = None, plugin_ids: list[str] = None, **_kwargs):
@@ -238,9 +261,9 @@ async def ask(self, content: str, conversation_id: uuid.UUID = None, parent_id:
238261

239262
timeout = httpx.Timeout(Config().openai_web.common_timeout, read=Config().openai_web.ask_timeout)
240263

241-
async with self.chatbot.session.stream(
264+
async with self.session.stream(
242265
method="POST",
243-
url=f"{self.chatbot.base_url}conversation",
266+
url=f"{config.openai_web.chatgpt_base_url}conversation",
244267
data=json.dumps(data),
245268
timeout=timeout,
246269
) as response:
@@ -267,18 +290,23 @@ async def ask(self, content: str, conversation_id: uuid.UUID = None, parent_id:
267290
yield line
268291

269292
async def delete_conversation(self, conversation_id: str):
270-
await self.chatbot.delete_conversation(conversation_id)
293+
# await self.chatbot.delete_conversation(conversation_id)
294+
url = f"{config.openai_web.chatgpt_base_url}conversation/{conversation_id}"
295+
response = await self.session.patch(url, data='{"is_visible": false}')
296+
await _check_response(response)
271297

272298
async def set_conversation_title(self, conversation_id: str, title: str):
273-
"""Hack change_title to set title in utf-8"""
274-
await self.chatbot.change_title(conversation_id, title)
299+
url = f"{config.openai_web.chatgpt_base_url}conversation/{conversation_id}"
300+
response = await self.session.patch(url, data=f'{{"title": "{title}"}}')
301+
await _check_response(response)
275302

276303
async def generate_conversation_title(self, conversation_id: str, message_id: str):
277-
"""Hack gen_title to get title"""
278-
await self.chatbot.gen_title(conversation_id, message_id)
279-
280-
def reset_chat(self):
281-
self.chatbot.reset_chat()
304+
url = f"{config.openai_web.chatgpt_base_url}conversation/gen_title/{conversation_id}"
305+
response = await self.session.post(
306+
url,
307+
data=json.dumps({"message_id": message_id, "model": "text-davinci-002-render"}),
308+
)
309+
await _check_response(response)
282310

283311
async def get_plugin_manifests(self, statuses="approved", is_installed=None, offset=0, limit=250):
284312
if not config.openai_web.is_plus_account:
@@ -290,8 +318,8 @@ async def get_plugin_manifests(self, statuses="approved", is_installed=None, off
290318
}
291319
if is_installed is not None:
292320
params["is_installed"] = is_installed
293-
response = await self.chatbot.session.get(
294-
url=f"{self.chatbot.base_url}aip/p",
321+
response = await self.session.get(
322+
url=f"{config.openai_web.chatgpt_base_url}aip/p",
295323
params=params,
296324
timeout=config.openai_web.ask_timeout
297325
)
@@ -301,8 +329,8 @@ async def get_plugin_manifests(self, statuses="approved", is_installed=None, off
301329
async def change_plugin_user_settings(self, plugin_id: str, setting: OpenaiChatPluginUserSettings):
302330
if not config.openai_web.is_plus_account:
303331
raise InvalidParamsException("errors.notPlusChatgptAccount")
304-
response = await self.chatbot.session.patch(
305-
url=f"{self.chatbot.base_url}aip/p/{plugin_id}/user-settings",
332+
response = await self.session.patch(
333+
url=f"{config.openai_web.chatgpt_base_url}aip/p/{plugin_id}/user-settings",
306334
json=setting.dict(exclude_unset=True, exclude_none=True),
307335
)
308336
await _check_response(response)

frontend/src/types/json/config_schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"default": {
88
"is_plus_account": false,
99
"chatgpt_base_url": null,
10+
"proxy": null,
1011
"common_timeout": 10,
1112
"ask_timeout": 600
1213
},
@@ -20,6 +21,7 @@
2021
"title": "Openai Api",
2122
"default": {
2223
"openai_base_url": "https://api.openai.com/v1/",
24+
"proxy": null,
2325
"connect_timeout": 10,
2426
"read_timeout": 20
2527
},
@@ -131,6 +133,10 @@
131133
"title": "Chatgpt Base Url",
132134
"type": "string"
133135
},
136+
"proxy": {
137+
"title": "Proxy",
138+
"type": "string"
139+
},
134140
"common_timeout": {
135141
"title": "Common Timeout",
136142
"default": 10,
@@ -154,6 +160,10 @@
154160
"default": "https://api.openai.com/v1/",
155161
"type": "string"
156162
},
163+
"proxy": {
164+
"title": "Proxy",
165+
"type": "string"
166+
},
157167
"connect_timeout": {
158168
"title": "Connect Timeout",
159169
"default": 10,

frontend/src/types/json/credentials_schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"title": "CredentialsModel",
33
"type": "object",
44
"properties": {
5-
"chatgpt_access_token": {
6-
"title": "Chatgpt Access Token",
5+
"openai_web_access_token": {
6+
"title": "Openai Web Access Token",
77
"type": "string"
88
},
99
"openai_api_key": {

frontend/src/types/json/openapi.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

frontend/src/types/json/schemas.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,10 @@
23382338
"title": "Chatgpt Base Url",
23392339
"type": "string"
23402340
},
2341+
"proxy": {
2342+
"title": "Proxy",
2343+
"type": "string"
2344+
},
23412345
"common_timeout": {
23422346
"title": "Common Timeout",
23432347
"minimum": 1,
@@ -2371,6 +2375,10 @@
23712375
"type": "string",
23722376
"default": "https://api.openai.com/v1/"
23732377
},
2378+
"proxy": {
2379+
"title": "Proxy",
2380+
"type": "string"
2381+
},
23742382
"connect_timeout": {
23752383
"title": "Connect Timeout",
23762384
"minimum": 1,
@@ -2633,8 +2641,8 @@
26332641
"title": "CredentialsModel",
26342642
"type": "object",
26352643
"properties": {
2636-
"chatgpt_access_token": {
2637-
"title": "Chatgpt Access Token",
2644+
"openai_web_access_token": {
2645+
"title": "Openai Web Access Token",
26382646
"type": "string"
26392647
},
26402648
"openai_api_key": {
@@ -3744,6 +3752,10 @@
37443752
"type": "string",
37453753
"default": "https://api.openai.com/v1/"
37463754
},
3755+
"proxy": {
3756+
"title": "Proxy",
3757+
"type": "string"
3758+
},
37473759
"connect_timeout": {
37483760
"title": "Connect Timeout",
37493761
"minimum": 1,
@@ -4141,6 +4153,10 @@
41414153
"title": "Chatgpt Base Url",
41424154
"type": "string"
41434155
},
4156+
"proxy": {
4157+
"title": "Proxy",
4158+
"type": "string"
4159+
},
41444160
"common_timeout": {
41454161
"title": "Common Timeout",
41464162
"minimum": 1,

0 commit comments

Comments
 (0)