mirror of https://github.com/xtekky/gpt4free.git
commit
c791012b21
|
@ -27,9 +27,9 @@ from ...typing import AsyncResult, Messages, Cookies, ImageType, Union, AsyncIte
|
||||||
from ...requests import get_args_from_browser, raise_for_status
|
from ...requests import get_args_from_browser, raise_for_status
|
||||||
from ...requests.aiohttp import StreamSession
|
from ...requests.aiohttp import StreamSession
|
||||||
from ...image import to_image, to_bytes, ImageResponse, ImageRequest
|
from ...image import to_image, to_bytes, ImageResponse, ImageRequest
|
||||||
from ...errors import MissingAuthError
|
from ...errors import MissingAuthError, ResponseError
|
||||||
from ...providers.conversation import BaseConversation
|
from ...providers.conversation import BaseConversation
|
||||||
from ..openai.har_file import getArkoseAndAccessToken
|
from ..openai.har_file import getArkoseAndAccessToken, NoValidHarFileError
|
||||||
from ... import debug
|
from ... import debug
|
||||||
|
|
||||||
class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
|
@ -37,7 +37,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
|
|
||||||
url = "https://chat.openai.com"
|
url = "https://chat.openai.com"
|
||||||
working = True
|
working = True
|
||||||
needs_auth = True
|
|
||||||
supports_gpt_35_turbo = True
|
supports_gpt_35_turbo = True
|
||||||
supports_gpt_4 = True
|
supports_gpt_4 = True
|
||||||
supports_message_history = True
|
supports_message_history = True
|
||||||
|
@ -56,6 +55,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
prompt: str = None,
|
prompt: str = None,
|
||||||
model: str = "",
|
model: str = "",
|
||||||
messages: Messages = [],
|
messages: Messages = [],
|
||||||
|
action: str = "next",
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> Response:
|
) -> Response:
|
||||||
"""
|
"""
|
||||||
|
@ -169,14 +169,17 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
The default model name as a string
|
The default model name as a string
|
||||||
"""
|
"""
|
||||||
if not cls.default_model:
|
if not cls.default_model:
|
||||||
async with session.get(f"{cls.url}/backend-api/models", headers=headers) as response:
|
url = f"{cls.url}/backend-anon/models" if cls._api_key is None else f"{cls.url}/backend-api/models"
|
||||||
|
async with session.get(url, headers=headers) as response:
|
||||||
cls._update_request_args(session)
|
cls._update_request_args(session)
|
||||||
|
if response.status == 401:
|
||||||
|
raise MissingAuthError('Add a "api_key" or a .har file' if cls._api_key is None else "Invalid api key")
|
||||||
await raise_for_status(response)
|
await raise_for_status(response)
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
if "categories" in data:
|
if "categories" in data:
|
||||||
cls.default_model = data["categories"][-1]["default_model"]
|
cls.default_model = data["categories"][-1]["default_model"]
|
||||||
return cls.default_model
|
return cls.default_model
|
||||||
raise RuntimeError(f"Response: {data}")
|
raise ResponseError(data)
|
||||||
return cls.default_model
|
return cls.default_model
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -330,39 +333,42 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If an error occurs during processing.
|
RuntimeError: If an error occurs during processing.
|
||||||
"""
|
"""
|
||||||
if parent_id is None:
|
|
||||||
parent_id = str(uuid.uuid4())
|
|
||||||
|
|
||||||
async with StreamSession(
|
async with StreamSession(
|
||||||
proxies={"https": proxy},
|
proxies={"https": proxy},
|
||||||
impersonate="chrome",
|
impersonate="chrome",
|
||||||
timeout=timeout
|
timeout=timeout
|
||||||
) as session:
|
) as session:
|
||||||
api_key = kwargs["access_token"] if "access_token" in kwargs else api_key
|
if cls._headers is None or cookies is not None:
|
||||||
|
|
||||||
if api_key is not None:
|
|
||||||
cls._create_request_args(cookies)
|
cls._create_request_args(cookies)
|
||||||
|
api_key = kwargs["access_token"] if "access_token" in kwargs else api_key
|
||||||
|
if api_key is not None:
|
||||||
cls._set_api_key(api_key)
|
cls._set_api_key(api_key)
|
||||||
|
|
||||||
if cls.default_model is None and cls._headers is not None:
|
if cls.default_model is None and cls._api_key is not None:
|
||||||
try:
|
try:
|
||||||
if not model:
|
if not model:
|
||||||
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
|
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
|
||||||
else:
|
else:
|
||||||
cls.default_model = cls.get_model(model)
|
cls.default_model = cls.get_model(model)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
api_key = cls._api_key = None
|
||||||
|
cls._create_request_args()
|
||||||
if debug.logging:
|
if debug.logging:
|
||||||
print("OpenaiChat: Load default_model failed")
|
print("OpenaiChat: Load default_model failed")
|
||||||
print(f"{e.__class__.__name__}: {e}")
|
print(f"{e.__class__.__name__}: {e}")
|
||||||
|
|
||||||
arkose_token = None
|
arkose_token = None
|
||||||
if cls.default_model is None:
|
if cls.default_model is None:
|
||||||
|
try:
|
||||||
arkose_token, api_key, cookies = await getArkoseAndAccessToken(proxy)
|
arkose_token, api_key, cookies = await getArkoseAndAccessToken(proxy)
|
||||||
cls._create_request_args(cookies)
|
cls._create_request_args(cookies)
|
||||||
cls._set_api_key(api_key)
|
cls._set_api_key(api_key)
|
||||||
|
except NoValidHarFileError:
|
||||||
|
...
|
||||||
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
|
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
|
||||||
|
|
||||||
async with session.post(
|
async with session.post(
|
||||||
|
f"{cls.url}/backend-anon/sentinel/chat-requirements" if not cls._api_key else
|
||||||
f"{cls.url}/backend-api/sentinel/chat-requirements",
|
f"{cls.url}/backend-api/sentinel/chat-requirements",
|
||||||
json={"conversation_mode_kind": "primary_assistant"},
|
json={"conversation_mode_kind": "primary_assistant"},
|
||||||
headers=cls._headers
|
headers=cls._headers
|
||||||
|
@ -389,17 +395,22 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
print(f"{e.__class__.__name__}: {e}")
|
print(f"{e.__class__.__name__}: {e}")
|
||||||
|
|
||||||
model = cls.get_model(model).replace("gpt-3.5-turbo", "text-davinci-002-render-sha")
|
model = cls.get_model(model).replace("gpt-3.5-turbo", "text-davinci-002-render-sha")
|
||||||
fields = Conversation(conversation_id, parent_id) if conversation is None else copy(conversation)
|
if conversation is None:
|
||||||
fields.finish_reason = None
|
conversation = Conversation(conversation_id, str(uuid.uuid4()) if parent_id is None else parent_id)
|
||||||
while fields.finish_reason is None:
|
else:
|
||||||
|
conversation = copy(conversation)
|
||||||
|
if cls._api_key is None:
|
||||||
|
auto_continue = False
|
||||||
|
conversation.finish_reason = None
|
||||||
|
while conversation.finish_reason is None:
|
||||||
websocket_request_id = str(uuid.uuid4())
|
websocket_request_id = str(uuid.uuid4())
|
||||||
data = {
|
data = {
|
||||||
"action": action,
|
"action": action,
|
||||||
"conversation_mode": {"kind": "primary_assistant"},
|
"conversation_mode": {"kind": "primary_assistant"},
|
||||||
"force_paragen": False,
|
"force_paragen": False,
|
||||||
"force_rate_limit": False,
|
"force_rate_limit": False,
|
||||||
"conversation_id": fields.conversation_id,
|
"conversation_id": conversation.conversation_id,
|
||||||
"parent_message_id": fields.message_id,
|
"parent_message_id": conversation.message_id,
|
||||||
"model": model,
|
"model": model,
|
||||||
"history_and_training_disabled": history_disabled and not auto_continue and not return_conversation,
|
"history_and_training_disabled": history_disabled and not auto_continue and not return_conversation,
|
||||||
"websocket_request_id": websocket_request_id
|
"websocket_request_id": websocket_request_id
|
||||||
|
@ -415,24 +426,27 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
if need_arkose:
|
if need_arkose:
|
||||||
headers["OpenAI-Sentinel-Arkose-Token"] = arkose_token
|
headers["OpenAI-Sentinel-Arkose-Token"] = arkose_token
|
||||||
async with session.post(
|
async with session.post(
|
||||||
|
f"{cls.url}/backend-anon/conversation" if cls._api_key is None else
|
||||||
f"{cls.url}/backend-api/conversation",
|
f"{cls.url}/backend-api/conversation",
|
||||||
json=data,
|
json=data,
|
||||||
headers=headers
|
headers=headers
|
||||||
) as response:
|
) as response:
|
||||||
cls._update_request_args(session)
|
cls._update_request_args(session)
|
||||||
await raise_for_status(response)
|
await raise_for_status(response)
|
||||||
async for chunk in cls.iter_messages_chunk(response.iter_lines(), session, fields):
|
async for chunk in cls.iter_messages_chunk(response.iter_lines(), session, conversation):
|
||||||
if return_conversation:
|
if return_conversation:
|
||||||
history_disabled = False
|
history_disabled = False
|
||||||
return_conversation = False
|
return_conversation = False
|
||||||
yield fields
|
yield conversation
|
||||||
yield chunk
|
yield chunk
|
||||||
if not auto_continue:
|
if auto_continue and conversation.finish_reason == "max_tokens":
|
||||||
break
|
conversation.finish_reason = None
|
||||||
action = "continue"
|
action = "continue"
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
else:
|
||||||
|
break
|
||||||
if history_disabled and auto_continue:
|
if history_disabled and auto_continue:
|
||||||
await cls.delete_conversation(session, cls._headers, fields.conversation_id)
|
await cls.delete_conversation(session, cls._headers, conversation.conversation_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def iter_messages_ws(ws: ClientWebSocketResponse, conversation_id: str, is_curl: bool) -> AsyncIterator:
|
async def iter_messages_ws(ws: ClientWebSocketResponse, conversation_id: str, is_curl: bool) -> AsyncIterator:
|
||||||
|
@ -594,15 +608,28 @@ this.fetch = async (url, options) => {
|
||||||
if "accessToken" in data:
|
if "accessToken" in data:
|
||||||
return data["accessToken"]
|
return data["accessToken"]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_default_headers() -> dict:
|
||||||
|
return {
|
||||||
|
"accept-language": "en-US",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"oai-device-id": str(uuid.uuid4()),
|
||||||
|
"oai-language": "en-US",
|
||||||
|
"sec-ch-ua": "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Google Chrome\";v=\"122\"",
|
||||||
|
"sec-ch-ua-mobile": "?0",
|
||||||
|
"sec-ch-ua-platform": "\"Linux\"",
|
||||||
|
"sec-fetch-dest": "empty",
|
||||||
|
"sec-fetch-mode": "cors",
|
||||||
|
"sec-fetch-site": "same-origin"
|
||||||
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_cookies(cookies: Cookies):
|
def _format_cookies(cookies: Cookies):
|
||||||
return "; ".join(f"{k}={v}" for k, v in cookies.items() if k != "access_token")
|
return "; ".join(f"{k}={v}" for k, v in cookies.items() if k != "access_token")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_request_args(cls, cookies: Union[Cookies, None]):
|
def _create_request_args(cls, cookies: Cookies = None):
|
||||||
cls._headers = {
|
cls._headers = cls.get_default_headers()
|
||||||
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
|
|
||||||
}
|
|
||||||
cls._cookies = {} if cookies is None else cookies
|
cls._cookies = {} if cookies is None else cookies
|
||||||
cls._update_cookie_header()
|
cls._update_cookie_header()
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ from copy import deepcopy
|
||||||
from .crypt import decrypt, encrypt
|
from .crypt import decrypt, encrypt
|
||||||
from ...requests import StreamSession
|
from ...requests import StreamSession
|
||||||
|
|
||||||
|
class NoValidHarFileError(Exception):
|
||||||
|
...
|
||||||
|
|
||||||
class arkReq:
|
class arkReq:
|
||||||
def __init__(self, arkURL, arkBx, arkHeader, arkBody, arkCookies, userAgent):
|
def __init__(self, arkURL, arkBx, arkHeader, arkBody, arkCookies, userAgent):
|
||||||
self.arkURL = arkURL
|
self.arkURL = arkURL
|
||||||
|
@ -39,7 +42,7 @@ def readHAR():
|
||||||
if harPath:
|
if harPath:
|
||||||
break
|
break
|
||||||
if not harPath:
|
if not harPath:
|
||||||
raise RuntimeError("No .har file found")
|
raise NoValidHarFileError("No .har file found")
|
||||||
for path in harPath:
|
for path in harPath:
|
||||||
with open(path, 'rb') as file:
|
with open(path, 'rb') as file:
|
||||||
try:
|
try:
|
||||||
|
@ -54,7 +57,7 @@ def readHAR():
|
||||||
accessToken = json.loads(v["response"]["content"]["text"]).get("accessToken")
|
accessToken = json.loads(v["response"]["content"]["text"]).get("accessToken")
|
||||||
cookies = {c['name']: c['value'] for c in v['request']['cookies']}
|
cookies = {c['name']: c['value'] for c in v['request']['cookies']}
|
||||||
if not accessToken:
|
if not accessToken:
|
||||||
RuntimeError("No accessToken found in .har files")
|
raise NoValidHarFileError("No accessToken found in .har files")
|
||||||
if not chatArks:
|
if not chatArks:
|
||||||
return None, accessToken, cookies
|
return None, accessToken, cookies
|
||||||
return chatArks.pop(), accessToken, cookies
|
return chatArks.pop(), accessToken, cookies
|
||||||
|
@ -75,9 +78,6 @@ def parseHAREntry(entry) -> arkReq:
|
||||||
return tmpArk
|
return tmpArk
|
||||||
|
|
||||||
def genArkReq(chatArk: arkReq) -> arkReq:
|
def genArkReq(chatArk: arkReq) -> arkReq:
|
||||||
if not chatArk:
|
|
||||||
raise RuntimeError("No .har file with arkose found")
|
|
||||||
|
|
||||||
tmpArk: arkReq = deepcopy(chatArk)
|
tmpArk: arkReq = deepcopy(chatArk)
|
||||||
if tmpArk is None or not tmpArk.arkBody or not tmpArk.arkHeader:
|
if tmpArk is None or not tmpArk.arkBody or not tmpArk.arkHeader:
|
||||||
raise RuntimeError("The .har file is not valid")
|
raise RuntimeError("The .har file is not valid")
|
||||||
|
|
|
@ -40,5 +40,8 @@ class NoImageResponseError(Exception):
|
||||||
class RateLimitError(Exception):
|
class RateLimitError(Exception):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
class ResponseError(Exception):
|
||||||
|
...
|
||||||
|
|
||||||
class ResponseStatusError(Exception):
|
class ResponseStatusError(Exception):
|
||||||
...
|
...
|
|
@ -10,19 +10,15 @@ from .Provider import (
|
||||||
ChatgptNext,
|
ChatgptNext,
|
||||||
HuggingChat,
|
HuggingChat,
|
||||||
HuggingFace,
|
HuggingFace,
|
||||||
ChatgptDemo,
|
OpenaiChat,
|
||||||
GptForLove,
|
|
||||||
ChatgptAi,
|
ChatgptAi,
|
||||||
DeepInfra,
|
DeepInfra,
|
||||||
ChatBase,
|
|
||||||
GigaChat,
|
GigaChat,
|
||||||
Liaobots,
|
Liaobots,
|
||||||
FreeGpt,
|
FreeGpt,
|
||||||
Llama2,
|
Llama2,
|
||||||
Vercel,
|
Vercel,
|
||||||
Gemini,
|
Gemini,
|
||||||
GptGo,
|
|
||||||
Gpt6,
|
|
||||||
Bing,
|
Bing,
|
||||||
You,
|
You,
|
||||||
Pi,
|
Pi,
|
||||||
|
@ -52,9 +48,10 @@ default = Model(
|
||||||
base_provider = "",
|
base_provider = "",
|
||||||
best_provider = RetryProvider([
|
best_provider = RetryProvider([
|
||||||
Bing,
|
Bing,
|
||||||
ChatgptAi, GptGo,
|
ChatgptAi,
|
||||||
You,
|
You,
|
||||||
Chatgpt4Online
|
Chatgpt4Online,
|
||||||
|
OpenaiChat
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,11 +60,10 @@ gpt_35_long = Model(
|
||||||
name = 'gpt-3.5-turbo',
|
name = 'gpt-3.5-turbo',
|
||||||
base_provider = 'openai',
|
base_provider = 'openai',
|
||||||
best_provider = RetryProvider([
|
best_provider = RetryProvider([
|
||||||
FreeGpt, You,
|
FreeGpt,
|
||||||
Chatgpt4Online,
|
You,
|
||||||
ChatgptNext,
|
ChatgptNext,
|
||||||
ChatgptDemo,
|
OpenaiChat,
|
||||||
Gpt6,
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,11 +71,7 @@ gpt_35_long = Model(
|
||||||
gpt_35_turbo = Model(
|
gpt_35_turbo = Model(
|
||||||
name = 'gpt-3.5-turbo',
|
name = 'gpt-3.5-turbo',
|
||||||
base_provider = 'openai',
|
base_provider = 'openai',
|
||||||
best_provider = RetryProvider([
|
best_provider = OpenaiChat
|
||||||
GptGo, You,
|
|
||||||
GptForLove, ChatBase,
|
|
||||||
Chatgpt4Online,
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
gpt_4 = Model(
|
gpt_4 = Model(
|
||||||
|
|
|
@ -13,12 +13,17 @@ class CloudflareError(ResponseStatusError):
|
||||||
def is_cloudflare(text: str) -> bool:
|
def is_cloudflare(text: str) -> bool:
|
||||||
return '<div id="cf-please-wait">' in text or "<title>Just a moment...</title>" in text
|
return '<div id="cf-please-wait">' in text or "<title>Just a moment...</title>" in text
|
||||||
|
|
||||||
|
def is_openai(text: str) -> bool:
|
||||||
|
return "<p>Unable to load site</p>" in text
|
||||||
|
|
||||||
async def raise_for_status_async(response: Union[StreamResponse, ClientResponse], message: str = None):
|
async def raise_for_status_async(response: Union[StreamResponse, ClientResponse], message: str = None):
|
||||||
if response.status in (429, 402):
|
if response.status in (429, 402):
|
||||||
raise RateLimitError(f"Response {response.status}: Rate limit reached")
|
raise RateLimitError(f"Response {response.status}: Rate limit reached")
|
||||||
message = await response.text() if not response.ok and message is None else message
|
message = await response.text() if not response.ok and message is None else message
|
||||||
if response.status == 403 and is_cloudflare(message):
|
if response.status == 403 and is_cloudflare(message):
|
||||||
raise CloudflareError(f"Response {response.status}: Cloudflare detected")
|
raise CloudflareError(f"Response {response.status}: Cloudflare detected")
|
||||||
|
elif response.status == 403 and is_openai(message):
|
||||||
|
raise ResponseStatusError(f"Response {response.status}: Bot are detected")
|
||||||
elif not response.ok:
|
elif not response.ok:
|
||||||
raise ResponseStatusError(f"Response {response.status}: {message}")
|
raise ResponseStatusError(f"Response {response.status}: {message}")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue