返回提交历史
Modified
g4f/Provider/Bing.py
+2
-3
Modified
g4f/Provider/You.py
+26
-30
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+1
-1
Modified
g4f/debug.py
+2
-1
Modified
g4f/gui/client/js/chat.v1.js
+2
-2
Modified
g4f/gui/server/app.py
+7
-1
Modified
g4f/gui/server/website.py
+7
-1
Modified
g4f/providers/base_provider.py
+9
-7
Modified
g4f/requests/__init__.py
+30
-1
Modified
g4f/requests/aiohttp.py
+6
-2
Modified
g4f/requests/defaults.py
+10
-0
Modified
g4f/version.py
+4
-0
Modified
requirements.txt
+0
-1
XFEstudio/gpt4free
Add Pyinstaller support, Use curl_cffi in You provider
8cc6000f
代码差异
13 个文件
+106
-50
@@ -406,14 +406,13 @@ async def stream_generate(
406
406
except Exception as e:
407
407
if debug.logging:
408
408
print(f"Bing: Failed to create images: {e}")
409
response_txt += f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
410
do_read = False
409
image_response = f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
411
410
if response_txt.startswith(returned_text):
412
411
new = response_txt[len(returned_text):]
413
412
if new not in ("", "\n"):
414
413
yield new
415
414
returned_text = response_txt
416
if image_response:
415
if image_response is not None:
417
416
yield image_response
418
417
elif response.get('type') == 2:
419
418
result = response['item']['result']
@@ -4,14 +4,18 @@ import re
4
4
import json
5
5
import base64
6
6
import uuid
7
from asyncio import get_running_loop
8
from aiohttp import ClientSession, FormData, BaseConnector, CookieJar
7
try:
8
from curl_cffi import CurlMime
9
has_curl_cffi = True
10
except ImportError:
11
has_curl_cffi = False
9
12
10
13
from ..typing import AsyncResult, Messages, ImageType, Cookies
11
14
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
12
from .helper import format_prompt, get_connector
15
from .helper import format_prompt
13
16
from ..image import to_bytes, ImageResponse
14
from ..requests import WebDriver, raise_for_status, get_args_from_browser
17
from ..requests import StreamSession, raise_for_status
18
from ..errors import MissingRequirementsError
15
19
16
20
class You(AsyncGeneratorProvider, ProviderModelMixin):
17
21
url = "https://you.com"
@@ -33,8 +37,6 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
33
37
model_aliases = {
34
38
"claude-v2": "claude-2"
35
39
}
36
_args: dict = None
37
_cookie_jar: CookieJar = None
38
40
_cookies = None
39
41
_cookies_used = 0
40
42
@@ -45,19 +47,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
45
47
messages: Messages,
46
48
image: ImageType = None,
47
49
image_name: str = None,
48
connector: BaseConnector = None,
49
webdriver: WebDriver = None,
50
50
proxy: str = None,
51
51
chat_mode: str = "default",
52
52
**kwargs,
53
53
) -> AsyncResult:
54
if cls._args is None:
55
cls._args = get_args_from_browser(cls.url, webdriver, proxy)
56
cls._cookie_jar = CookieJar(loop=get_running_loop())
57
else:
58
if "cookies" in cls._args:
59
del cls._args["cookies"]
60
cls._cookie_jar._loop = get_running_loop()
54
if not has_curl_cffi:
55
raise MissingRequirementsError('Install "curl_cffi" package')
61
56
if image is not None:
62
57
chat_mode = "agent"
63
58
elif not model or model == cls.default_model:
@@ -67,10 +62,9 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
67
62
else:
68
63
chat_mode = "custom"
69
64
model = cls.get_model(model)
70
async with ClientSession(
71
connector=get_connector(connector, proxy),
72
cookie_jar=cls._cookie_jar,
73
**cls._args
65
async with StreamSession(
66
proxy=proxy,
67
impersonate="chrome"
74
68
) as session:
75
69
cookies = await cls.get_cookies(session) if chat_mode != "default" else None
76
70
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):
82
76
# and idx < len(questions)
83
77
# ]
84
78
headers = {
85
"accept": "text/event-stream",
86
"referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
79
"Accept": "text/event-stream",
80
"Referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
87
81
}
88
82
data = {
89
83
"userFiles": upload,
@@ -106,12 +100,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
106
100
cookies=cookies
107
101
) as response:
108
102
await raise_for_status(response)
109
async for line in response.content:
103
async for line in response.iter_lines():
110
104
if line.startswith(b'event: '):
111
event = line[7:-1].decode()
105
event = line[7:].decode()
112
106
elif line.startswith(b'data: '):
113
107
if event in ["youChatUpdate", "youChatToken"]:
114
data = json.loads(line[6:-1])
108
data = json.loads(line[6:])
115
109
if event == "youChatToken" and event in data:
116
110
yield data[event]
117
111
elif event == "youChatUpdate" and "t" in data:
@@ -122,18 +116,20 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
122
116
yield data["t"]
123
117
124
118
@classmethod
125
async def upload_file(cls, client: ClientSession, cookies: Cookies, file: bytes, filename: str = None) -> dict:
119
async def upload_file(cls, client: StreamSession, cookies: Cookies, file: bytes, filename: str = None) -> dict:
126
120
async with client.get(
127
121
f"{cls.url}/api/get_nonce",
128
122
cookies=cookies,
129
123
) as response:
130
124
await raise_for_status(response)
131
125
upload_nonce = await response.text()
132
data = FormData()
133
data.add_field('file', file, filename=filename)
126
#data = FormData()
127
#data.add_field('file', file, filename=filename)
128
multipart = CurlMime()
129
multipart.addpart(name="file", filename=filename, data=file)
134
130
async with client.post(
135
131
f"{cls.url}/api/upload",
136
data=data,
132
multipart=multipart,
137
133
headers={
138
134
"X-Upload-Nonce": upload_nonce,
139
135
},
@@ -146,7 +142,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
146
142
return result
147
143
148
144
@classmethod
149
async def get_cookies(cls, client: ClientSession) -> Cookies:
145
async def get_cookies(cls, client: StreamSession) -> Cookies:
150
146
if not cls._cookies or cls._cookies_used >= 5:
151
147
cls._cookies = await cls.create_cookies(client)
152
148
cls._cookies_used = 0
@@ -173,7 +169,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
173
169
return f"Basic {auth}"
174
170
175
171
@classmethod
176
async def create_cookies(cls, client: ClientSession) -> Cookies:
172
async def create_cookies(cls, client: StreamSession) -> Cookies:
177
173
user_uuid = str(uuid.uuid4())
178
174
async with client.post(
179
175
"https://web.stytch.com/sdk/v1/passwords",
@@ -572,7 +572,7 @@ this.fetch = async (url, options) => {
572
572
while headers is None:
573
573
headers = window.evaluate_js("this._headers")
574
574
await asyncio.sleep(1)
575
headers["User-Agent"] = window.evaluate_js("window.navigator.userAgent")
575
headers["User-Agent"] = window.evaluate_js("this.navigator.userAgent")
576
576
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
577
577
window.destroy()
578
578
cls._cookies = dict([(name, cookie.value) for name, cookie in cookies])
@@ -3,4 +3,5 @@ from .providers.types import ProviderType
3
3
logging: bool = False
4
4
version_check: bool = True
5
5
last_provider: ProviderType = None
6
last_model: str = None
6
last_model: str = None
7
version: str = None
@@ -916,16 +916,16 @@ fileInput.addEventListener('change', async (event) => {
916
916
reader.addEventListener('load', async (event) => {
917
917
fileInput.dataset.text = event.target.result;
918
918
if (type == "json") {
919
const data = JSON.parse(event.target.result);
919
const data = JSON.parse(fileInput.dataset.text);
920
920
if ("g4f" in data.options) {
921
921
Object.keys(data).forEach(key => {
922
922
if (key != "options" && !localStorage.getItem(key)) {
923
923
appStorage.setItem(key, JSON.stringify(data[key]));
924
924
}
925
925
});
926
fileInput.value = "";
927
926
delete fileInput.dataset.text;
928
927
await load_conversations();
928
fileInput.value = "";
929
929
}
930
930
}
931
931
});
@@ -1,3 +1,9 @@
1
import sys, os
1
2
from flask import Flask
2
3
3
app = Flask(__name__, template_folder='./../client/html')
4
if getattr(sys, 'frozen', False):
5
template_folder = os.path.join(sys._MEIPASS, "client/html")
6
else:
7
template_folder = "./../client/html"
8
9
app = Flask(__name__, template_folder=template_folder)
@@ -1,6 +1,12 @@
1
1
from flask import render_template, send_file, redirect
2
2
from time import time
3
3
from os import urandom
4
import sys, os
5
6
if getattr(sys, 'frozen', False):
7
assets_folder = os.path.join(sys._MEIPASS, "client")
8
else:
9
assets_folder = "./../client"
4
10
5
11
class Website:
6
12
def __init__(self, app) -> None:
@@ -35,6 +41,6 @@ class Website:
35
41
36
42
def _assets(self, folder: str, file: str):
37
43
try:
38
return send_file(f"./../client/{folder}/{file}", as_attachment=False)
44
return send_file(f"{assets_folder}/{folder}/{file}", as_attachment=False)
39
45
except:
40
46
return "File not found", 404
@@ -70,7 +70,14 @@ class AbstractProvider(BaseProvider):
70
70
loop.run_in_executor(executor, create_func),
71
71
timeout=kwargs.get("timeout")
72
72
)
73
73
74
def get_parameters(cls) -> dict:
75
return signature(
76
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
77
cls.create_async if issubclass(cls, AsyncProvider) else
78
cls.create_completion
79
).parameters
80
74
81
@classmethod
75
82
@property
76
83
def params(cls) -> str:
@@ -83,17 +90,12 @@ class AbstractProvider(BaseProvider):
83
90
Returns:
84
91
str: A string listing the supported parameters.
85
92
"""
86
sig = signature(
87
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
88
cls.create_async if issubclass(cls, AsyncProvider) else
89
cls.create_completion
90
)
91
93
92
94
def get_type_name(annotation: type) -> str:
93
95
return annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
94
96
95
97
args = ""
96
for name, param in sig.parameters.items():
98
for name, param in cls.get_parameters():
97
99
if name in ("self", "kwargs") or (name == "stream" and not cls.supports_stream):
98
100
continue
99
101
args += f"\n {name}"
@@ -12,11 +12,40 @@ except ImportError:
12
12
from typing import Type as Session, Type as Response
13
13
from .aiohttp import StreamResponse, StreamSession
14
14
has_curl_cffi = False
15
try:
16
import webview
17
import asyncio
18
has_webview = True
19
except ImportError:
20
has_webview = False
15
21
16
22
from ..webdriver import WebDriver, WebDriverSession
17
23
from ..webdriver import bypass_cloudflare, get_driver_cookies
18
24
from ..errors import MissingRequirementsError, RateLimitError, ResponseStatusError
19
from .defaults import DEFAULT_HEADERS
25
from .defaults import DEFAULT_HEADERS, WEBVIEW_HAEDERS
26
27
async def get_args_from_webview(url: str):
28
if not has_webview:
29
raise MissingRequirementsError('Install "webview" package')
30
window = webview.create_window("", url, hidden=True)
31
await asyncio.sleep(2)
32
body = None
33
while body is None:
34
try:
35
await asyncio.sleep(1)
36
body = window.dom.get_element("body:not(.no-js)")
37
except:
38
...
39
headers = {
40
**WEBVIEW_HAEDERS,
41
"User-Agent": window.evaluate_js("this.navigator.userAgent"),
42
"Accept-Language": window.evaluate_js("this.navigator.language"),
43
"Referer": window.real_url
44
}
45
cookies = [list(*cookie.items()) for cookie in window.get_cookies()]
46
cookies = dict([(name, cookie.value) for name, cookie in cookies])
47
window.destroy()
48
return {"headers": headers, "cookies": cookies}
20
49
21
50
def get_args_from_browser(
22
51
url: str,
@@ -1,6 +1,6 @@
1
1
from __future__ import annotations
2
2
3
from aiohttp import ClientSession, ClientResponse, ClientTimeout, BaseConnector
3
from aiohttp import ClientSession, ClientResponse, ClientTimeout, BaseConnector, FormData
4
4
from typing import AsyncIterator, Any, Optional
5
5
6
6
from .defaults import DEFAULT_HEADERS
@@ -43,4 +43,8 @@ def get_connector(connector: BaseConnector = None, proxy: str = None, rdns: bool
43
43
connector = ProxyConnector.from_url(proxy, rdns=rdns)
44
44
except ImportError:
45
45
raise MissingRequirementsError('Install "aiohttp_socks" package for proxy support')
46
return connector
46
return connector
47
48
class CurlMime(FormData):
49
def addpart(self, name: str, content_type: str = None, filename: str = None, data: bytes = None):
50
self.add_field(name, data, content_type=content_type, filename=filename)
@@ -16,4 +16,14 @@ DEFAULT_HEADERS = {
16
16
"referer": "",
17
17
"accept-encoding": "gzip, deflate, br",
18
18
"accept-language": "en-US",
19
}
20
WEBVIEW_HAEDERS = {
21
"Accept": "*/*",
22
"Accept-Encoding": "gzip, deflate, br",
23
"Accept-Language": "",
24
"Referer": "",
25
"Sec-Fetch-Dest": "empty",
26
"Sec-Fetch-Mode": "cors",
27
"Sec-Fetch-Site": "same-origin",
28
"User-Agent": "",
19
29
}
@@ -6,6 +6,7 @@ from functools import cached_property
6
6
from importlib.metadata import version as get_package_version, PackageNotFoundError
7
7
from subprocess import check_output, CalledProcessError, PIPE
8
8
from .errors import VersionNotFoundError
9
from . import debug
9
10
10
11
PACKAGE_NAME = "g4f"
11
12
GITHUB_REPOSITORY = "xtekky/gpt4free"
@@ -64,6 +65,9 @@ class VersionUtils:
64
65
VersionNotFoundError: If the version cannot be determined from the package manager,
65
66
Docker environment, or git repository.
66
67
"""
68
if debug.version:
69
return debug.version
70
67
71
# Read from package manager
68
72
try:
69
73
return get_package_version(PACKAGE_NAME)