mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-06 02:30:41 -08:00
Add Pyinstaller support, Use curl_cffi in You provider
This commit is contained in:
parent
95b1b8c025
commit
8cc6000ffb
13 changed files with 106 additions and 50 deletions
|
|
@ -406,14 +406,13 @@ async def stream_generate(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if debug.logging:
|
if debug.logging:
|
||||||
print(f"Bing: Failed to create images: {e}")
|
print(f"Bing: Failed to create images: {e}")
|
||||||
response_txt += f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
|
image_response = f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
|
||||||
do_read = False
|
|
||||||
if response_txt.startswith(returned_text):
|
if response_txt.startswith(returned_text):
|
||||||
new = response_txt[len(returned_text):]
|
new = response_txt[len(returned_text):]
|
||||||
if new not in ("", "\n"):
|
if new not in ("", "\n"):
|
||||||
yield new
|
yield new
|
||||||
returned_text = response_txt
|
returned_text = response_txt
|
||||||
if image_response:
|
if image_response is not None:
|
||||||
yield image_response
|
yield image_response
|
||||||
elif response.get('type') == 2:
|
elif response.get('type') == 2:
|
||||||
result = response['item']['result']
|
result = response['item']['result']
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,18 @@ import re
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
import uuid
|
import uuid
|
||||||
from asyncio import get_running_loop
|
try:
|
||||||
from aiohttp import ClientSession, FormData, BaseConnector, CookieJar
|
from curl_cffi import CurlMime
|
||||||
|
has_curl_cffi = True
|
||||||
|
except ImportError:
|
||||||
|
has_curl_cffi = False
|
||||||
|
|
||||||
from ..typing import AsyncResult, Messages, ImageType, Cookies
|
from ..typing import AsyncResult, Messages, ImageType, Cookies
|
||||||
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
||||||
from .helper import format_prompt, get_connector
|
from .helper import format_prompt
|
||||||
from ..image import to_bytes, ImageResponse
|
from ..image import to_bytes, ImageResponse
|
||||||
from ..requests import WebDriver, raise_for_status, get_args_from_browser
|
from ..requests import StreamSession, raise_for_status
|
||||||
|
from ..errors import MissingRequirementsError
|
||||||
|
|
||||||
class You(AsyncGeneratorProvider, ProviderModelMixin):
|
class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
url = "https://you.com"
|
url = "https://you.com"
|
||||||
|
|
@ -33,8 +37,6 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
model_aliases = {
|
model_aliases = {
|
||||||
"claude-v2": "claude-2"
|
"claude-v2": "claude-2"
|
||||||
}
|
}
|
||||||
_args: dict = None
|
|
||||||
_cookie_jar: CookieJar = None
|
|
||||||
_cookies = None
|
_cookies = None
|
||||||
_cookies_used = 0
|
_cookies_used = 0
|
||||||
|
|
||||||
|
|
@ -45,19 +47,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
messages: Messages,
|
messages: Messages,
|
||||||
image: ImageType = None,
|
image: ImageType = None,
|
||||||
image_name: str = None,
|
image_name: str = None,
|
||||||
connector: BaseConnector = None,
|
|
||||||
webdriver: WebDriver = None,
|
|
||||||
proxy: str = None,
|
proxy: str = None,
|
||||||
chat_mode: str = "default",
|
chat_mode: str = "default",
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
if cls._args is None:
|
if not has_curl_cffi:
|
||||||
cls._args = get_args_from_browser(cls.url, webdriver, proxy)
|
raise MissingRequirementsError('Install "curl_cffi" package')
|
||||||
cls._cookie_jar = CookieJar(loop=get_running_loop())
|
|
||||||
else:
|
|
||||||
if "cookies" in cls._args:
|
|
||||||
del cls._args["cookies"]
|
|
||||||
cls._cookie_jar._loop = get_running_loop()
|
|
||||||
if image is not None:
|
if image is not None:
|
||||||
chat_mode = "agent"
|
chat_mode = "agent"
|
||||||
elif not model or model == cls.default_model:
|
elif not model or model == cls.default_model:
|
||||||
|
|
@ -67,10 +62,9 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
else:
|
else:
|
||||||
chat_mode = "custom"
|
chat_mode = "custom"
|
||||||
model = cls.get_model(model)
|
model = cls.get_model(model)
|
||||||
async with ClientSession(
|
async with StreamSession(
|
||||||
connector=get_connector(connector, proxy),
|
proxy=proxy,
|
||||||
cookie_jar=cls._cookie_jar,
|
impersonate="chrome"
|
||||||
**cls._args
|
|
||||||
) as session:
|
) as session:
|
||||||
cookies = await cls.get_cookies(session) if chat_mode != "default" else None
|
cookies = await cls.get_cookies(session) if chat_mode != "default" else None
|
||||||
upload = json.dumps([await cls.upload_file(session, cookies, to_bytes(image), image_name)]) if image else ""
|
upload = json.dumps([await cls.upload_file(session, cookies, to_bytes(image), image_name)]) if image else ""
|
||||||
|
|
@ -82,8 +76,8 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
# and idx < len(questions)
|
# and idx < len(questions)
|
||||||
# ]
|
# ]
|
||||||
headers = {
|
headers = {
|
||||||
"accept": "text/event-stream",
|
"Accept": "text/event-stream",
|
||||||
"referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
|
"Referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
|
||||||
}
|
}
|
||||||
data = {
|
data = {
|
||||||
"userFiles": upload,
|
"userFiles": upload,
|
||||||
|
|
@ -106,12 +100,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
cookies=cookies
|
cookies=cookies
|
||||||
) as response:
|
) as response:
|
||||||
await raise_for_status(response)
|
await raise_for_status(response)
|
||||||
async for line in response.content:
|
async for line in response.iter_lines():
|
||||||
if line.startswith(b'event: '):
|
if line.startswith(b'event: '):
|
||||||
event = line[7:-1].decode()
|
event = line[7:].decode()
|
||||||
elif line.startswith(b'data: '):
|
elif line.startswith(b'data: '):
|
||||||
if event in ["youChatUpdate", "youChatToken"]:
|
if event in ["youChatUpdate", "youChatToken"]:
|
||||||
data = json.loads(line[6:-1])
|
data = json.loads(line[6:])
|
||||||
if event == "youChatToken" and event in data:
|
if event == "youChatToken" and event in data:
|
||||||
yield data[event]
|
yield data[event]
|
||||||
elif event == "youChatUpdate" and "t" in data:
|
elif event == "youChatUpdate" and "t" in data:
|
||||||
|
|
@ -122,18 +116,20 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
yield data["t"]
|
yield data["t"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def upload_file(cls, client: ClientSession, cookies: Cookies, file: bytes, filename: str = None) -> dict:
|
async def upload_file(cls, client: StreamSession, cookies: Cookies, file: bytes, filename: str = None) -> dict:
|
||||||
async with client.get(
|
async with client.get(
|
||||||
f"{cls.url}/api/get_nonce",
|
f"{cls.url}/api/get_nonce",
|
||||||
cookies=cookies,
|
cookies=cookies,
|
||||||
) as response:
|
) as response:
|
||||||
await raise_for_status(response)
|
await raise_for_status(response)
|
||||||
upload_nonce = await response.text()
|
upload_nonce = await response.text()
|
||||||
data = FormData()
|
#data = FormData()
|
||||||
data.add_field('file', file, filename=filename)
|
#data.add_field('file', file, filename=filename)
|
||||||
|
multipart = CurlMime()
|
||||||
|
multipart.addpart(name="file", filename=filename, data=file)
|
||||||
async with client.post(
|
async with client.post(
|
||||||
f"{cls.url}/api/upload",
|
f"{cls.url}/api/upload",
|
||||||
data=data,
|
multipart=multipart,
|
||||||
headers={
|
headers={
|
||||||
"X-Upload-Nonce": upload_nonce,
|
"X-Upload-Nonce": upload_nonce,
|
||||||
},
|
},
|
||||||
|
|
@ -146,7 +142,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_cookies(cls, client: ClientSession) -> Cookies:
|
async def get_cookies(cls, client: StreamSession) -> Cookies:
|
||||||
if not cls._cookies or cls._cookies_used >= 5:
|
if not cls._cookies or cls._cookies_used >= 5:
|
||||||
cls._cookies = await cls.create_cookies(client)
|
cls._cookies = await cls.create_cookies(client)
|
||||||
cls._cookies_used = 0
|
cls._cookies_used = 0
|
||||||
|
|
@ -173,7 +169,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
return f"Basic {auth}"
|
return f"Basic {auth}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_cookies(cls, client: ClientSession) -> Cookies:
|
async def create_cookies(cls, client: StreamSession) -> Cookies:
|
||||||
user_uuid = str(uuid.uuid4())
|
user_uuid = str(uuid.uuid4())
|
||||||
async with client.post(
|
async with client.post(
|
||||||
"https://web.stytch.com/sdk/v1/passwords",
|
"https://web.stytch.com/sdk/v1/passwords",
|
||||||
|
|
|
||||||
|
|
@ -572,7 +572,7 @@ this.fetch = async (url, options) => {
|
||||||
while headers is None:
|
while headers is None:
|
||||||
headers = window.evaluate_js("this._headers")
|
headers = window.evaluate_js("this._headers")
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
headers["User-Agent"] = window.evaluate_js("window.navigator.userAgent")
|
headers["User-Agent"] = window.evaluate_js("this.navigator.userAgent")
|
||||||
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
|
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
|
||||||
window.destroy()
|
window.destroy()
|
||||||
cls._cookies = dict([(name, cookie.value) for name, cookie in cookies])
|
cls._cookies = dict([(name, cookie.value) for name, cookie in cookies])
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ from .providers.types import ProviderType
|
||||||
logging: bool = False
|
logging: bool = False
|
||||||
version_check: bool = True
|
version_check: bool = True
|
||||||
last_provider: ProviderType = None
|
last_provider: ProviderType = None
|
||||||
last_model: str = None
|
last_model: str = None
|
||||||
|
version: str = None
|
||||||
|
|
@ -916,16 +916,16 @@ fileInput.addEventListener('change', async (event) => {
|
||||||
reader.addEventListener('load', async (event) => {
|
reader.addEventListener('load', async (event) => {
|
||||||
fileInput.dataset.text = event.target.result;
|
fileInput.dataset.text = event.target.result;
|
||||||
if (type == "json") {
|
if (type == "json") {
|
||||||
const data = JSON.parse(event.target.result);
|
const data = JSON.parse(fileInput.dataset.text);
|
||||||
if ("g4f" in data.options) {
|
if ("g4f" in data.options) {
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
if (key != "options" && !localStorage.getItem(key)) {
|
if (key != "options" && !localStorage.getItem(key)) {
|
||||||
appStorage.setItem(key, JSON.stringify(data[key]));
|
appStorage.setItem(key, JSON.stringify(data[key]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
fileInput.value = "";
|
|
||||||
delete fileInput.dataset.text;
|
delete fileInput.dataset.text;
|
||||||
await load_conversations();
|
await load_conversations();
|
||||||
|
fileInput.value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
import sys, os
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
app = Flask(__name__, template_folder='./../client/html')
|
if getattr(sys, 'frozen', False):
|
||||||
|
template_folder = os.path.join(sys._MEIPASS, "client/html")
|
||||||
|
else:
|
||||||
|
template_folder = "./../client/html"
|
||||||
|
|
||||||
|
app = Flask(__name__, template_folder=template_folder)
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
from flask import render_template, send_file, redirect
|
from flask import render_template, send_file, redirect
|
||||||
from time import time
|
from time import time
|
||||||
from os import urandom
|
from os import urandom
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
assets_folder = os.path.join(sys._MEIPASS, "client")
|
||||||
|
else:
|
||||||
|
assets_folder = "./../client"
|
||||||
|
|
||||||
class Website:
|
class Website:
|
||||||
def __init__(self, app) -> None:
|
def __init__(self, app) -> None:
|
||||||
|
|
@ -35,6 +41,6 @@ class Website:
|
||||||
|
|
||||||
def _assets(self, folder: str, file: str):
|
def _assets(self, folder: str, file: str):
|
||||||
try:
|
try:
|
||||||
return send_file(f"./../client/{folder}/{file}", as_attachment=False)
|
return send_file(f"{assets_folder}/{folder}/{file}", as_attachment=False)
|
||||||
except:
|
except:
|
||||||
return "File not found", 404
|
return "File not found", 404
|
||||||
|
|
@ -70,7 +70,14 @@ class AbstractProvider(BaseProvider):
|
||||||
loop.run_in_executor(executor, create_func),
|
loop.run_in_executor(executor, create_func),
|
||||||
timeout=kwargs.get("timeout")
|
timeout=kwargs.get("timeout")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_parameters(cls) -> dict:
|
||||||
|
return signature(
|
||||||
|
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
|
||||||
|
cls.create_async if issubclass(cls, AsyncProvider) else
|
||||||
|
cls.create_completion
|
||||||
|
).parameters
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@property
|
@property
|
||||||
def params(cls) -> str:
|
def params(cls) -> str:
|
||||||
|
|
@ -83,17 +90,12 @@ class AbstractProvider(BaseProvider):
|
||||||
Returns:
|
Returns:
|
||||||
str: A string listing the supported parameters.
|
str: A string listing the supported parameters.
|
||||||
"""
|
"""
|
||||||
sig = signature(
|
|
||||||
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
|
|
||||||
cls.create_async if issubclass(cls, AsyncProvider) else
|
|
||||||
cls.create_completion
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_type_name(annotation: type) -> str:
|
def get_type_name(annotation: type) -> str:
|
||||||
return annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
|
return annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
|
||||||
|
|
||||||
args = ""
|
args = ""
|
||||||
for name, param in sig.parameters.items():
|
for name, param in cls.get_parameters():
|
||||||
if name in ("self", "kwargs") or (name == "stream" and not cls.supports_stream):
|
if name in ("self", "kwargs") or (name == "stream" and not cls.supports_stream):
|
||||||
continue
|
continue
|
||||||
args += f"\n {name}"
|
args += f"\n {name}"
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,40 @@ except ImportError:
|
||||||
from typing import Type as Session, Type as Response
|
from typing import Type as Session, Type as Response
|
||||||
from .aiohttp import StreamResponse, StreamSession
|
from .aiohttp import StreamResponse, StreamSession
|
||||||
has_curl_cffi = False
|
has_curl_cffi = False
|
||||||
|
try:
|
||||||
|
import webview
|
||||||
|
import asyncio
|
||||||
|
has_webview = True
|
||||||
|
except ImportError:
|
||||||
|
has_webview = False
|
||||||
|
|
||||||
from ..webdriver import WebDriver, WebDriverSession
|
from ..webdriver import WebDriver, WebDriverSession
|
||||||
from ..webdriver import bypass_cloudflare, get_driver_cookies
|
from ..webdriver import bypass_cloudflare, get_driver_cookies
|
||||||
from ..errors import MissingRequirementsError, RateLimitError, ResponseStatusError
|
from ..errors import MissingRequirementsError, RateLimitError, ResponseStatusError
|
||||||
from .defaults import DEFAULT_HEADERS
|
from .defaults import DEFAULT_HEADERS, WEBVIEW_HAEDERS
|
||||||
|
|
||||||
|
async def get_args_from_webview(url: str):
|
||||||
|
if not has_webview:
|
||||||
|
raise MissingRequirementsError('Install "webview" package')
|
||||||
|
window = webview.create_window("", url, hidden=True)
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
body = None
|
||||||
|
while body is None:
|
||||||
|
try:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
body = window.dom.get_element("body:not(.no-js)")
|
||||||
|
except:
|
||||||
|
...
|
||||||
|
headers = {
|
||||||
|
**WEBVIEW_HAEDERS,
|
||||||
|
"User-Agent": window.evaluate_js("this.navigator.userAgent"),
|
||||||
|
"Accept-Language": window.evaluate_js("this.navigator.language"),
|
||||||
|
"Referer": window.real_url
|
||||||
|
}
|
||||||
|
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
|
||||||
|
cookies = dict([(name, cookie.value) for name, cookie in cookies])
|
||||||
|
window.destroy()
|
||||||
|
return {"headers": headers, "cookies": cookies}
|
||||||
|
|
||||||
def get_args_from_browser(
|
def get_args_from_browser(
|
||||||
url: str,
|
url: str,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from aiohttp import ClientSession, ClientResponse, ClientTimeout, BaseConnector
|
from aiohttp import ClientSession, ClientResponse, ClientTimeout, BaseConnector, FormData
|
||||||
from typing import AsyncIterator, Any, Optional
|
from typing import AsyncIterator, Any, Optional
|
||||||
|
|
||||||
from .defaults import DEFAULT_HEADERS
|
from .defaults import DEFAULT_HEADERS
|
||||||
|
|
@ -43,4 +43,8 @@ def get_connector(connector: BaseConnector = None, proxy: str = None, rdns: bool
|
||||||
connector = ProxyConnector.from_url(proxy, rdns=rdns)
|
connector = ProxyConnector.from_url(proxy, rdns=rdns)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise MissingRequirementsError('Install "aiohttp_socks" package for proxy support')
|
raise MissingRequirementsError('Install "aiohttp_socks" package for proxy support')
|
||||||
return connector
|
return connector
|
||||||
|
|
||||||
|
class CurlMime(FormData):
|
||||||
|
def addpart(self, name: str, content_type: str = None, filename: str = None, data: bytes = None):
|
||||||
|
self.add_field(name, data, content_type=content_type, filename=filename)
|
||||||
|
|
@ -16,4 +16,14 @@ DEFAULT_HEADERS = {
|
||||||
"referer": "",
|
"referer": "",
|
||||||
"accept-encoding": "gzip, deflate, br",
|
"accept-encoding": "gzip, deflate, br",
|
||||||
"accept-language": "en-US",
|
"accept-language": "en-US",
|
||||||
|
}
|
||||||
|
WEBVIEW_HAEDERS = {
|
||||||
|
"Accept": "*/*",
|
||||||
|
"Accept-Encoding": "gzip, deflate, br",
|
||||||
|
"Accept-Language": "",
|
||||||
|
"Referer": "",
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Site": "same-origin",
|
||||||
|
"User-Agent": "",
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ from functools import cached_property
|
||||||
from importlib.metadata import version as get_package_version, PackageNotFoundError
|
from importlib.metadata import version as get_package_version, PackageNotFoundError
|
||||||
from subprocess import check_output, CalledProcessError, PIPE
|
from subprocess import check_output, CalledProcessError, PIPE
|
||||||
from .errors import VersionNotFoundError
|
from .errors import VersionNotFoundError
|
||||||
|
from . import debug
|
||||||
|
|
||||||
PACKAGE_NAME = "g4f"
|
PACKAGE_NAME = "g4f"
|
||||||
GITHUB_REPOSITORY = "xtekky/gpt4free"
|
GITHUB_REPOSITORY = "xtekky/gpt4free"
|
||||||
|
|
@ -64,6 +65,9 @@ class VersionUtils:
|
||||||
VersionNotFoundError: If the version cannot be determined from the package manager,
|
VersionNotFoundError: If the version cannot be determined from the package manager,
|
||||||
Docker environment, or git repository.
|
Docker environment, or git repository.
|
||||||
"""
|
"""
|
||||||
|
if debug.version:
|
||||||
|
return debug.version
|
||||||
|
|
||||||
# Read from package manager
|
# Read from package manager
|
||||||
try:
|
try:
|
||||||
return get_package_version(PACKAGE_NAME)
|
return get_package_version(PACKAGE_NAME)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ fastapi
|
||||||
uvicorn
|
uvicorn
|
||||||
flask
|
flask
|
||||||
py-arkose-generator
|
py-arkose-generator
|
||||||
async-property
|
|
||||||
undetected-chromedriver>=3.5.5
|
undetected-chromedriver>=3.5.5
|
||||||
brotli
|
brotli
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue