summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiner Lohaus <hlohaus@users.noreply.github.com>2024-03-15 11:46:06 +0100
committerHeiner Lohaus <hlohaus@users.noreply.github.com>2024-03-15 11:46:06 +0100
commit8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0 (patch)
treeea190c7017d8e8982e63a502da0a828d5ba42f94
parentAdd export / import conversations (diff)
downloadgpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar.gz
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar.bz2
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar.lz
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar.xz
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.tar.zst
gpt4free-8cc6000ffbf4e12bf6c1d5e5878d376e36857ec0.zip
-rw-r--r--g4f/Provider/Bing.py5
-rw-r--r--g4f/Provider/You.py56
-rw-r--r--g4f/Provider/needs_auth/OpenaiChat.py2
-rw-r--r--g4f/debug.py3
-rw-r--r--g4f/gui/client/js/chat.v1.js4
-rw-r--r--g4f/gui/server/app.py8
-rw-r--r--g4f/gui/server/website.py8
-rw-r--r--g4f/providers/base_provider.py16
-rw-r--r--g4f/requests/__init__.py31
-rw-r--r--g4f/requests/aiohttp.py8
-rw-r--r--g4f/requests/defaults.py10
-rw-r--r--g4f/version.py4
-rw-r--r--requirements.txt1
13 files changed, 106 insertions, 50 deletions
diff --git a/g4f/Provider/Bing.py b/g4f/Provider/Bing.py
index f8b13020..925dae0a 100644
--- a/g4f/Provider/Bing.py
+++ b/g4f/Provider/Bing.py
@@ -406,14 +406,13 @@ async def stream_generate(
except Exception as e:
if debug.logging:
print(f"Bing: Failed to create images: {e}")
- response_txt += f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
- do_read = False
+ image_response = f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}"
if response_txt.startswith(returned_text):
new = response_txt[len(returned_text):]
if new not in ("", "\n"):
yield new
returned_text = response_txt
- if image_response:
+ if image_response is not None:
yield image_response
elif response.get('type') == 2:
result = response['item']['result']
diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py
index 85b60452..8adc8b19 100644
--- a/g4f/Provider/You.py
+++ b/g4f/Provider/You.py
@@ -4,14 +4,18 @@ import re
import json
import base64
import uuid
-from asyncio import get_running_loop
-from aiohttp import ClientSession, FormData, BaseConnector, CookieJar
+try:
+ from curl_cffi import CurlMime
+ has_curl_cffi = True
+except ImportError:
+ has_curl_cffi = False
from ..typing import AsyncResult, Messages, ImageType, Cookies
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 ..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):
url = "https://you.com"
@@ -33,8 +37,6 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
model_aliases = {
"claude-v2": "claude-2"
}
- _args: dict = None
- _cookie_jar: CookieJar = None
_cookies = None
_cookies_used = 0
@@ -45,19 +47,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
messages: Messages,
image: ImageType = None,
image_name: str = None,
- connector: BaseConnector = None,
- webdriver: WebDriver = None,
proxy: str = None,
chat_mode: str = "default",
**kwargs,
) -> AsyncResult:
- if cls._args is None:
- cls._args = get_args_from_browser(cls.url, webdriver, proxy)
- 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 not has_curl_cffi:
+ raise MissingRequirementsError('Install "curl_cffi" package')
if image is not None:
chat_mode = "agent"
elif not model or model == cls.default_model:
@@ -67,10 +62,9 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
else:
chat_mode = "custom"
model = cls.get_model(model)
- async with ClientSession(
- connector=get_connector(connector, proxy),
- cookie_jar=cls._cookie_jar,
- **cls._args
+ async with StreamSession(
+ proxy=proxy,
+ impersonate="chrome"
) as session:
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 ""
@@ -82,8 +76,8 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
# and idx < len(questions)
# ]
headers = {
- "accept": "text/event-stream",
- "referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
+ "Accept": "text/event-stream",
+ "Referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
}
data = {
"userFiles": upload,
@@ -106,12 +100,12 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
cookies=cookies
) as response:
await raise_for_status(response)
- async for line in response.content:
+ async for line in response.iter_lines():
if line.startswith(b'event: '):
- event = line[7:-1].decode()
+ event = line[7:].decode()
elif line.startswith(b'data: '):
if event in ["youChatUpdate", "youChatToken"]:
- data = json.loads(line[6:-1])
+ data = json.loads(line[6:])
if event == "youChatToken" and event in data:
yield data[event]
elif event == "youChatUpdate" and "t" in data:
@@ -122,18 +116,20 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
yield data["t"]
@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(
f"{cls.url}/api/get_nonce",
cookies=cookies,
) as response:
await raise_for_status(response)
upload_nonce = await response.text()
- data = FormData()
- data.add_field('file', file, filename=filename)
+ #data = FormData()
+ #data.add_field('file', file, filename=filename)
+ multipart = CurlMime()
+ multipart.addpart(name="file", filename=filename, data=file)
async with client.post(
f"{cls.url}/api/upload",
- data=data,
+ multipart=multipart,
headers={
"X-Upload-Nonce": upload_nonce,
},
@@ -146,7 +142,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
return result
@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:
cls._cookies = await cls.create_cookies(client)
cls._cookies_used = 0
@@ -173,7 +169,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
return f"Basic {auth}"
@classmethod
- async def create_cookies(cls, client: ClientSession) -> Cookies:
+ async def create_cookies(cls, client: StreamSession) -> Cookies:
user_uuid = str(uuid.uuid4())
async with client.post(
"https://web.stytch.com/sdk/v1/passwords",
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index b501d655..6601f500 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -572,7 +572,7 @@ this.fetch = async (url, options) => {
while headers is None:
headers = window.evaluate_js("this._headers")
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()]
window.destroy()
cls._cookies = dict([(name, cookie.value) for name, cookie in cookies])
diff --git a/g4f/debug.py b/g4f/debug.py
index 69f7d55c..3f08e9e8 100644
--- a/g4f/debug.py
+++ b/g4f/debug.py
@@ -3,4 +3,5 @@ from .providers.types import ProviderType
logging: bool = False
version_check: bool = True
last_provider: ProviderType = None
-last_model: str = None \ No newline at end of file
+last_model: str = None
+version: str = None \ No newline at end of file
diff --git a/g4f/gui/client/js/chat.v1.js b/g4f/gui/client/js/chat.v1.js
index 24253b2b..c0d0cb6d 100644
--- a/g4f/gui/client/js/chat.v1.js
+++ b/g4f/gui/client/js/chat.v1.js
@@ -916,16 +916,16 @@ fileInput.addEventListener('change', async (event) => {
reader.addEventListener('load', async (event) => {
fileInput.dataset.text = event.target.result;
if (type == "json") {
- const data = JSON.parse(event.target.result);
+ const data = JSON.parse(fileInput.dataset.text);
if ("g4f" in data.options) {
Object.keys(data).forEach(key => {
if (key != "options" && !localStorage.getItem(key)) {
appStorage.setItem(key, JSON.stringify(data[key]));
}
});
- fileInput.value = "";
delete fileInput.dataset.text;
await load_conversations();
+ fileInput.value = "";
}
}
});
diff --git a/g4f/gui/server/app.py b/g4f/gui/server/app.py
index 2b55698c..d9e25ca1 100644
--- a/g4f/gui/server/app.py
+++ b/g4f/gui/server/app.py
@@ -1,3 +1,9 @@
+import sys, os
from flask import Flask
-app = Flask(__name__, template_folder='./../client/html') \ No newline at end of file
+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) \ No newline at end of file
diff --git a/g4f/gui/server/website.py b/g4f/gui/server/website.py
index 2705664d..9aa98b76 100644
--- a/g4f/gui/server/website.py
+++ b/g4f/gui/server/website.py
@@ -1,6 +1,12 @@
from flask import render_template, send_file, redirect
from time import time
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:
def __init__(self, app) -> None:
@@ -35,6 +41,6 @@ class Website:
def _assets(self, folder: str, file: str):
try:
- return send_file(f"./../client/{folder}/{file}", as_attachment=False)
+ return send_file(f"{assets_folder}/{folder}/{file}", as_attachment=False)
except:
return "File not found", 404 \ No newline at end of file
diff --git a/g4f/providers/base_provider.py b/g4f/providers/base_provider.py
index 32a0c01b..c8397193 100644
--- a/g4f/providers/base_provider.py
+++ b/g4f/providers/base_provider.py
@@ -70,7 +70,14 @@ class AbstractProvider(BaseProvider):
loop.run_in_executor(executor, create_func),
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
@property
def params(cls) -> str:
@@ -83,17 +90,12 @@ class AbstractProvider(BaseProvider):
Returns:
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:
return annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
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):
continue
args += f"\n {name}"
diff --git a/g4f/requests/__init__.py b/g4f/requests/__init__.py
index 3bb68e9c..cfc6af42 100644
--- a/g4f/requests/__init__.py
+++ b/g4f/requests/__init__.py
@@ -12,11 +12,40 @@ except ImportError:
from typing import Type as Session, Type as Response
from .aiohttp import StreamResponse, StreamSession
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 bypass_cloudflare, get_driver_cookies
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(
url: str,
diff --git a/g4f/requests/aiohttp.py b/g4f/requests/aiohttp.py
index 6979b20a..505086a1 100644
--- a/g4f/requests/aiohttp.py
+++ b/g4f/requests/aiohttp.py
@@ -1,6 +1,6 @@
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 .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)
except ImportError:
raise MissingRequirementsError('Install "aiohttp_socks" package for proxy support')
- return connector \ No newline at end of file
+ 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) \ No newline at end of file
diff --git a/g4f/requests/defaults.py b/g4f/requests/defaults.py
index 2457f046..3183eb5a 100644
--- a/g4f/requests/defaults.py
+++ b/g4f/requests/defaults.py
@@ -16,4 +16,14 @@ DEFAULT_HEADERS = {
"referer": "",
"accept-encoding": "gzip, deflate, br",
"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": "",
} \ No newline at end of file
diff --git a/g4f/version.py b/g4f/version.py
index 0d85a7f5..eda2b8fe 100644
--- a/g4f/version.py
+++ b/g4f/version.py
@@ -6,6 +6,7 @@ from functools import cached_property
from importlib.metadata import version as get_package_version, PackageNotFoundError
from subprocess import check_output, CalledProcessError, PIPE
from .errors import VersionNotFoundError
+from . import debug
PACKAGE_NAME = "g4f"
GITHUB_REPOSITORY = "xtekky/gpt4free"
@@ -64,6 +65,9 @@ class VersionUtils:
VersionNotFoundError: If the version cannot be determined from the package manager,
Docker environment, or git repository.
"""
+ if debug.version:
+ return debug.version
+
# Read from package manager
try:
return get_package_version(PACKAGE_NAME)
diff --git a/requirements.txt b/requirements.txt
index 1a7ded34..def8c7e3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,7 +15,6 @@ fastapi
uvicorn
flask
py-arkose-generator
-async-property
undetected-chromedriver>=3.5.5
brotli
beautifulsoup4