XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Add Pyinstaller support, Use curl_cffi in You provider

8cc6000f
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

13 个文件 +106 -50
Modified g4f/Provider/Bing.py +2 -3
@@ -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']
Modified g4f/Provider/You.py +26 -30
@@ -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",
Modified g4f/Provider/needs_auth/OpenaiChat.py +1 -1
@@ -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])
Modified g4f/debug.py +2 -1
@@ -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
Modified g4f/gui/client/js/chat.v1.js +2 -2
@@ -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 });
Modified g4f/gui/server/app.py +7 -1
@@ -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)
Modified g4f/gui/server/website.py +7 -1
@@ -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
Modified g4f/providers/base_provider.py +9 -7
@@ -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}"
Modified g4f/requests/__init__.py +30 -1
@@ -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,
Modified g4f/requests/aiohttp.py +6 -2
@@ -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)
Modified g4f/requests/defaults.py +10 -0
@@ -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 }
Modified g4f/version.py +4 -0
@@ -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)
Modified requirements.txt +0 -1