返回提交历史
Modified
etc/testing/test_chat_completion.py
+5
-6
Modified
g4f/Provider/Bing.py
+5
-13
Modified
g4f/Provider/base_provider.py
+3
-4
Modified
g4f/Provider/bing/conversation.py
+5
-2
Modified
g4f/Provider/bing/create_images.py
+7
-11
Modified
g4f/Provider/bing/upload_image.py
+47
-119
Modified
g4f/Provider/create_images.py
+7
-3
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+242
-102
Modified
g4f/__init__.py
+1
-1
Modified
g4f/base_provider.py
+1
-1
Modified
g4f/gui/client/css/style.css
+26
-8
Modified
g4f/gui/client/html/index.html
+18
-75
Modified
g4f/gui/client/js/chat.v1.js
+51
-45
Modified
g4f/gui/server/backend.py
+42
-16
Added
g4f/image.py
+116
-0
Modified
g4f/requests.py
+17
-14
Modified
g4f/typing.py
+3
-1
Modified
g4f/version.py
+14
-12
Modified
requirements.txt
+0
-4
Modified
setup.py
+0
-4
XFEstudio/gpt4free
Support upload image in gui Add image upload to OpenaiChat Add image response to OpenaiChat Improve ChatGPT Plus Support Remove unused requirements
ea8d6b84
代码差异
20 个文件
+610
-441
@@ -7,10 +7,9 @@ import g4f, asyncio
7
7
8
8
print("create:", end=" ", flush=True)
9
9
for response in g4f.ChatCompletion.create(
10
model=g4f.models.gpt_4_32k_0613,
11
provider=g4f.Provider.Aivvm,
10
model=g4f.models.default,
11
provider=g4f.Provider.Bing,
12
12
messages=[{"role": "user", "content": "write a poem about a tree"}],
13
temperature=0.1,
14
13
stream=True
15
14
):
16
15
print(response, end="", flush=True)
@@ -18,10 +17,10 @@ print()
18
17
19
18
async def run_async():
20
19
response = await g4f.ChatCompletion.create_async(
21
model=g4f.models.gpt_35_turbo_16k_0613,
22
provider=g4f.Provider.GptGod,
20
model=g4f.models.default,
21
provider=g4f.Provider.Bing,
23
22
messages=[{"role": "user", "content": "hello!"}],
24
23
)
25
24
print("create_async:", response)
26
25
27
# asyncio.run(run_async())
26
asyncio.run(run_async())
@@ -8,11 +8,10 @@ import time
8
8
from urllib import parse
9
9
from aiohttp import ClientSession, ClientTimeout
10
10
11
from ..typing import AsyncResult, Messages
11
from ..typing import AsyncResult, Messages, ImageType
12
12
from .base_provider import AsyncGeneratorProvider
13
from ..webdriver import get_browser, get_driver_cookies
14
13
from .bing.upload_image import upload_image
15
from .bing.create_images import create_images, format_images_markdown, wait_for_login
14
from .bing.create_images import create_images, format_images_markdown
16
15
from .bing.conversation import Conversation, create_conversation, delete_conversation
17
16
18
17
class Tones():
@@ -34,7 +33,7 @@ class Bing(AsyncGeneratorProvider):
34
33
timeout: int = 900,
35
34
cookies: dict = None,
36
35
tone: str = Tones.balanced,
37
image: str = None,
36
image: ImageType = None,
38
37
web_search: bool = False,
39
38
**kwargs
40
39
) -> AsyncResult:
@@ -247,7 +246,7 @@ def create_message(
247
246
async def stream_generate(
248
247
prompt: str,
249
248
tone: str,
250
image: str = None,
249
image: ImageType = None,
251
250
context: str = None,
252
251
proxy: str = None,
253
252
cookies: dict = None,
@@ -315,14 +314,7 @@ async def stream_generate(
315
314
result = response['item']['result']
316
315
if result.get('error'):
317
316
if result["value"] == "CaptchaChallenge":
318
driver = get_browser(proxy=proxy)
319
try:
320
wait_for_login(driver)
321
cookies = get_driver_cookies(driver)
322
finally:
323
driver.quit()
324
async for chunk in stream_generate(prompt, tone, image, context, proxy, cookies, web_search, gpt4_turbo, timeout):
325
yield chunk
317
raise Exception(f"{result['value']}: Use other cookies or/and ip address")
326
318
else:
327
319
raise Exception(f"{result['value']}: {result['message']}")
328
320
return
@@ -7,7 +7,7 @@ from concurrent.futures import ThreadPoolExecutor
7
7
from abc import abstractmethod
8
8
from inspect import signature, Parameter
9
9
from .helper import get_event_loop, get_cookies, format_prompt
10
from ..typing import CreateResult, AsyncResult, Messages, Union
10
from ..typing import CreateResult, AsyncResult, Messages
11
11
from ..base_provider import BaseProvider
12
12
13
13
if sys.version_info < (3, 10):
@@ -77,8 +77,7 @@ class AbstractProvider(BaseProvider):
77
77
continue
78
78
if args:
79
79
args += ", "
80
args += "\n"
81
args += " " + name
80
args += "\n " + name
82
81
if name != "model" and param.annotation is not Parameter.empty:
83
82
args += f": {get_type_name(param.annotation)}"
84
83
if param.default == "":
@@ -156,7 +155,7 @@ class AsyncGeneratorProvider(AsyncProvider):
156
155
messages,
157
156
stream=False,
158
157
**kwargs
159
)
158
) if not isinstance(chunk, Exception)
160
159
])
161
160
162
161
@staticmethod
@@ -10,7 +10,10 @@ class Conversation():
10
10
async def create_conversation(session: ClientSession, proxy: str = None) -> Conversation:
11
11
url = 'https://www.bing.com/turing/conversation/create?bundleVersion=1.1199.4'
12
12
async with session.get(url, proxy=proxy) as response:
13
data = await response.json()
13
try:
14
data = await response.json()
15
except:
16
raise RuntimeError(f"Response: {await response.text()}")
14
17
15
18
conversationId = data.get('conversationId')
16
19
clientId = data.get('clientId')
@@ -26,7 +29,7 @@ async def list_conversations(session: ClientSession) -> list:
26
29
response = await response.json()
27
30
return response["chats"]
28
31
29
async def delete_conversation(session: ClientSession, conversation: Conversation, proxy: str = None) -> list:
32
async def delete_conversation(session: ClientSession, conversation: Conversation, proxy: str = None) -> bool:
30
33
url = "https://sydney.bing.com/sydney/DeleteSingleConversation"
31
34
json = {
32
35
"conversationId": conversation.conversationId,
@@ -9,6 +9,7 @@ from ..create_images import CreateImagesProvider
9
9
from ..helper import get_cookies, get_event_loop
10
10
from ...webdriver import WebDriver, get_driver_cookies, get_browser
11
11
from ...base_provider import ProviderType
12
from ...image import format_images_markdown
12
13
13
14
BING_URL = "https://www.bing.com"
14
15
@@ -23,6 +24,7 @@ def wait_for_login(driver: WebDriver, timeout: int = 1200) -> None:
23
24
raise RuntimeError("Timeout error")
24
25
value = driver.get_cookie("_U")
25
26
if value:
27
time.sleep(1)
26
28
return
27
29
time.sleep(0.5)
28
30
@@ -62,7 +64,8 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
62
64
errors = [
63
65
"this prompt is being reviewed",
64
66
"this prompt has been blocked",
65
"we're working hard to offer image creator in more languages"
67
"we're working hard to offer image creator in more languages",
68
"we can't create your images right now"
66
69
]
67
70
text = (await response.text()).lower()
68
71
for error in errors:
@@ -72,7 +75,7 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
72
75
url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=3&FORM=GENCRE"
73
76
async with session.post(url, allow_redirects=False, proxy=proxy, timeout=timeout) as response:
74
77
if response.status != 302:
75
raise RuntimeError(f"Create images failed. Status Code: {response.status}")
78
raise RuntimeError(f"Create images failed. Code: {response.status}")
76
79
77
80
redirect_url = response.headers["Location"].replace("&nfy=1", "")
78
81
redirect_url = f"{BING_URL}{redirect_url}"
@@ -84,10 +87,10 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
84
87
start_time = time.time()
85
88
while True:
86
89
if time.time() - start_time > timeout:
87
raise RuntimeError(f"Timeout error after {timeout} seconds")
90
raise RuntimeError(f"Timeout error after {timeout} sec")
88
91
async with session.get(polling_url) as response:
89
92
if response.status != 200:
90
raise RuntimeError(f"Polling images faild. Status Code: {response.status}")
93
raise RuntimeError(f"Polling images faild. Code: {response.status}")
91
94
text = await response.text()
92
95
if not text:
93
96
await asyncio.sleep(1)
@@ -119,13 +122,6 @@ def read_images(text: str) -> list:
119
122
raise RuntimeError("No images found")
120
123
return images
121
124
122
def format_images_markdown(images: list, prompt: str) -> str:
123
images = [f"[]({image})" for idx, image in enumerate(images)]
124
images = "\n".join(images)
125
start_flag = "<!-- generated images start -->\n"
126
end_flag = "<!-- generated images end -->\n"
127
return f"\n{start_flag}{images}\n{end_flag}\n"
128
129
125
async def create_images_markdown(cookies: dict, prompt: str, proxy: str = None) -> str:
130
126
session = create_session(cookies)
131
127
try:
@@ -3,70 +3,59 @@ from __future__ import annotations
3
3
import string
4
4
import random
5
5
import json
6
import re
7
import io
8
import base64
9
6
import numpy as np
10
from PIL import Image
7
from ...typing import ImageType
11
8
from aiohttp import ClientSession
9
from ...image import to_image, process_image, to_base64
10
11
image_config = {
12
"maxImagePixels": 360000,
13
"imageCompressionRate": 0.7,
14
"enableFaceBlurDebug": 0,
15
}
12
16
13
17
async def upload_image(
14
18
session: ClientSession,
15
image: str,
19
image: ImageType,
16
20
tone: str,
17
21
proxy: str = None
18
):
19
try:
20
image_config = {
21
"maxImagePixels": 360000,
22
"imageCompressionRate": 0.7,
23
"enableFaceBlurDebug": 0,
24
}
25
is_data_uri_an_image(image)
26
img_binary_data = extract_data_uri(image)
27
is_accepted_format(img_binary_data)
28
img = Image.open(io.BytesIO(img_binary_data))
29
width, height = img.size
30
max_image_pixels = image_config['maxImagePixels']
31
if max_image_pixels / (width * height) < 1:
32
new_width = int(width * np.sqrt(max_image_pixels / (width * height)))
33
new_height = int(height * np.sqrt(max_image_pixels / (width * height)))
34
else:
35
new_width = width
36
new_height = height
37
try:
38
orientation = get_orientation(img)
39
except Exception:
40
orientation = None
41
new_img = process_image(orientation, img, new_width, new_height)
42
new_img_binary_data = compress_image_to_base64(new_img, image_config['imageCompressionRate'])
43
data, boundary = build_image_upload_api_payload(new_img_binary_data, tone)
44
headers = session.headers.copy()
45
headers["content-type"] = f'multipart/form-data; boundary={boundary}'
46
headers["referer"] = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
47
headers["origin"] = 'https://www.bing.com'
48
async with session.post("https://www.bing.com/images/kblob", data=data, headers=headers, proxy=proxy) as response:
49
if response.status != 200:
50
raise RuntimeError("Failed to upload image.")
51
image_info = await response.json()
52
if not image_info.get('blobId'):
53
raise RuntimeError("Failed to parse image info.")
54
result = {'bcid': image_info.get('blobId', "")}
55
result['blurredBcid'] = image_info.get('processedBlobId', "")
56
if result['blurredBcid'] != "":
57
result["imageUrl"] = "https://www.bing.com/images/blob?bcid=" + result['blurredBcid']
58
elif result['bcid'] != "":
59
result["imageUrl"] = "https://www.bing.com/images/blob?bcid=" + result['bcid']
60
result['originalImageUrl'] = (
61
"https://www.bing.com/images/blob?bcid="
62
+ result['blurredBcid']
63
if image_config["enableFaceBlurDebug"]
64
else "https://www.bing.com/images/blob?bcid="
65
+ result['bcid']
66
)
67
return result
68
except Exception as e:
69
raise RuntimeError(f"Upload image failed: {e}")
22
) -> dict:
23
image = to_image(image)
24
width, height = image.size
25
max_image_pixels = image_config['maxImagePixels']
26
if max_image_pixels / (width * height) < 1:
27
new_width = int(width * np.sqrt(max_image_pixels / (width * height)))
28
new_height = int(height * np.sqrt(max_image_pixels / (width * height)))
29
else:
30
new_width = width
31
new_height = height
32
new_img = process_image(image, new_width, new_height)
33
new_img_binary_data = to_base64(new_img, image_config['imageCompressionRate'])
34
data, boundary = build_image_upload_api_payload(new_img_binary_data, tone)
35
headers = session.headers.copy()
36
headers["content-type"] = f'multipart/form-data; boundary={boundary}'
37
headers["referer"] = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
38
headers["origin"] = 'https://www.bing.com'
39
async with session.post("https://www.bing.com/images/kblob", data=data, headers=headers, proxy=proxy) as response:
40
if response.status != 200:
41
raise RuntimeError("Failed to upload image.")
42
image_info = await response.json()
43
if not image_info.get('blobId'):
44
raise RuntimeError("Failed to parse image info.")
45
result = {'bcid': image_info.get('blobId', "")}
46
result['blurredBcid'] = image_info.get('processedBlobId', "")
47
if result['blurredBcid'] != "":
48
result["imageUrl"] = "https://www.bing.com/images/blob?bcid=" + result['blurredBcid']
49
elif result['bcid'] != "":
50
result["imageUrl"] = "https://www.bing.com/images/blob?bcid=" + result['bcid']
51
result['originalImageUrl'] = (
52
"https://www.bing.com/images/blob?bcid="
53
+ result['blurredBcid']
54
if image_config["enableFaceBlurDebug"]
55
else "https://www.bing.com/images/blob?bcid="
56
+ result['bcid']
57
)
58
return result
70
59
71
60
72
61
def build_image_upload_api_payload(image_bin: str, tone: str):
@@ -98,65 +87,4 @@ def build_image_upload_api_payload(image_bin: str, tone: str):
98
87
+ boundary
99
88
+ "--\r\n"
100
89
)
101
return data, boundary
102
103
def is_data_uri_an_image(data_uri: str):
104
# Check if the data URI starts with 'data:image' and contains an image format (e.g., jpeg, png, gif)
105
if not re.match(r'data:image/(\w+);base64,', data_uri):
106
raise ValueError("Invalid data URI image.")
107
# Extract the image format from the data URI
108
image_format = re.match(r'data:image/(\w+);base64,', data_uri).group(1)
109
# Check if the image format is one of the allowed formats (jpg, jpeg, png, gif)
110
if image_format.lower() not in ['jpeg', 'jpg', 'png', 'gif']:
111
raise ValueError("Invalid image format (from mime file type).")
112
113
def is_accepted_format(binary_data: bytes) -> bool:
114
if binary_data.startswith(b'\xFF\xD8\xFF'):
115
pass # It's a JPEG image
116
elif binary_data.startswith(b'\x89PNG\r\n\x1a\n'):
117
pass # It's a PNG image
118
elif binary_data.startswith(b'GIF87a') or binary_data.startswith(b'GIF89a'):
119
pass # It's a GIF image
120
elif binary_data.startswith(b'\x89JFIF') or binary_data.startswith(b'JFIF\x00'):
121
pass # It's a JPEG image
122
elif binary_data.startswith(b'\xFF\xD8'):
123
pass # It's a JPEG image
124
elif binary_data.startswith(b'RIFF') and binary_data[8:12] == b'WEBP':
125
pass # It's a WebP image
126
else:
127
raise ValueError("Invalid image format (from magic code).")
128
129
def extract_data_uri(data_uri: str) -> bytes:
130
data = data_uri.split(",")[1]
131
data = base64.b64decode(data)
132
return data
133
134
def get_orientation(data: bytes) -> int:
135
if data[:2] != b'\xFF\xD8':
136
raise Exception('NotJpeg')
137
with Image.open(data) as img:
138
exif_data = img._getexif()
139
if exif_data is not None:
140
orientation = exif_data.get(274) # 274 corresponds to the orientation tag in EXIF
141
if orientation is not None:
142
return orientation
143
144
def process_image(orientation: int, img: Image.Image, new_width: int, new_height: int) -> Image.Image:
145
# Initialize the canvas
146
new_img = Image.new("RGB", (new_width, new_height), color="#FFFFFF")
147
if orientation:
148
if orientation > 4:
149
img = img.transpose(Image.FLIP_LEFT_RIGHT)
150
if orientation in [3, 4]:
151
img = img.transpose(Image.ROTATE_180)
152
if orientation in [5, 6]:
153
img = img.transpose(Image.ROTATE_270)
154
if orientation in [7, 8]:
155
img = img.transpose(Image.ROTATE_90)
156
new_img.paste(img, (0, 0))
157
return new_img
158
159
def compress_image_to_base64(image: Image.Image, compression_rate: float) -> str:
160
output_buffer = io.BytesIO()
161
image.save(output_buffer, format="JPEG", quality=int(compression_rate * 100))
162
return base64.b64encode(output_buffer.getvalue()).decode('utf-8')
90
return data, boundary
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
3
3
import re
4
4
import asyncio
5
from .. import debug
5
6
from ..typing import CreateResult, Messages
6
7
from ..base_provider import BaseProvider, ProviderType
7
8
@@ -26,12 +27,11 @@ class CreateImagesProvider(BaseProvider):
26
27
self.create_images = create_images
27
28
self.create_images_async = create_async
28
29
self.system_message = system_message
30
self.include_placeholder = include_placeholder
29
31
self.__name__ = provider.__name__
32
self.url = provider.url
30
33
self.working = provider.working
31
34
self.supports_stream = provider.supports_stream
32
self.include_placeholder = include_placeholder
33
if hasattr(provider, "url"):
34
self.url = provider.url
35
35
36
36
def create_completion(
37
37
self,
@@ -54,6 +54,8 @@ class CreateImagesProvider(BaseProvider):
54
54
yield start
55
55
if self.include_placeholder:
56
56
yield placeholder
57
if debug.logging:
58
print(f"Create images with prompt: {prompt}")
57
59
yield from self.create_images(prompt)
58
60
if append:
59
61
yield append
@@ -76,6 +78,8 @@ class CreateImagesProvider(BaseProvider):
76
78
placeholders = []
77
79
for placeholder, prompt in matches:
78
80
if placeholder not in placeholders:
81
if debug.logging:
82
print(f"Create images with prompt: {prompt}")
79
83
results.append(self.create_images_async(prompt))
80
84
placeholders.append(placeholder)
81
85
results = await asyncio.gather(*results)
@@ -2,17 +2,18 @@ from __future__ import annotations
2
2
3
3
import uuid, json, asyncio, os
4
4
from py_arkose_generator.arkose import get_values_for_request
5
from asyncstdlib.itertools import tee
6
5
from async_property import async_cached_property
7
6
from selenium.webdriver.common.by import By
8
7
from selenium.webdriver.support.ui import WebDriverWait
9
8
from selenium.webdriver.support import expected_conditions as EC
10
9
11
10
from ..base_provider import AsyncGeneratorProvider
12
from ..helper import get_event_loop, format_prompt, get_cookies
13
from ...webdriver import get_browser
11
from ..helper import format_prompt, get_cookies
12
from ...webdriver import get_browser, get_driver_cookies
14
13
from ...typing import AsyncResult, Messages
15
14
from ...requests import StreamSession
15
from ...image import to_image, to_bytes, ImageType, ImageResponse
16
from ... import debug
16
17
17
18
models = {
18
19
"gpt-3.5": "text-davinci-002-render-sha",
@@ -28,6 +29,7 @@ class OpenaiChat(AsyncGeneratorProvider):
28
29
supports_gpt_35_turbo = True
29
30
supports_gpt_4 = True
30
31
_cookies: dict = {}
32
_default_model: str = None
31
33
32
34
@classmethod
33
35
async def create(
@@ -39,6 +41,7 @@ class OpenaiChat(AsyncGeneratorProvider):
39
41
action: str = "next",
40
42
conversation_id: str = None,
41
43
parent_id: str = None,
44
image: ImageType = None,
42
45
**kwargs
43
46
) -> Response:
44
47
if prompt:
@@ -53,16 +56,120 @@ class OpenaiChat(AsyncGeneratorProvider):
53
56
action=action,
54
57
conversation_id=conversation_id,
55
58
parent_id=parent_id,
59
image=image,
56
60
response_fields=True,
57
61
**kwargs
58
62
)
59
63
return Response(
60
64
generator,
61
await anext(generator),
62
65
action,
63
66
messages,
64
67
kwargs
65
68
)
69
70
@classmethod
71
async def upload_image(
72
cls,
73
session: StreamSession,
74
headers: dict,
75
image: ImageType
76
) -> ImageResponse:
77
image = to_image(image)
78
extension = image.format.lower()
79
data_bytes = to_bytes(image)
80
data = {
81
"file_name": f"{image.width}x{image.height}.{extension}",
82
"file_size": len(data_bytes),
83
"use_case": "multimodal"
84
}
85
async with session.post(f"{cls.url}/backend-api/files", json=data, headers=headers) as response:
86
response.raise_for_status()
87
image_data = {
88
**data,
89
**await response.json(),
90
"mime_type": f"image/{extension}",
91
"extension": extension,
92
"height": image.height,
93
"width": image.width
94
}
95
async with session.put(
96
image_data["upload_url"],
97
data=data_bytes,
98
headers={
99
"Content-Type": image_data["mime_type"],
100
"x-ms-blob-type": "BlockBlob"
101
}
102
) as response:
103
response.raise_for_status()
104
async with session.post(
105
f"{cls.url}/backend-api/files/{image_data['file_id']}/uploaded",
106
json={},
107
headers=headers
108
) as response:
109
response.raise_for_status()
110
download_url = (await response.json())["download_url"]
111
return ImageResponse(download_url, image_data["file_name"], image_data)
112
113
@classmethod
114
async def get_default_model(cls, session: StreamSession, headers: dict):
115
if cls._default_model:
116
model = cls._default_model
117
else:
118
async with session.get(f"{cls.url}/backend-api/models", headers=headers) as response:
119
data = await response.json()
120
if "categories" in data:
121
model = data["categories"][-1]["default_model"]
122
else:
123
RuntimeError(f"Response: {data}")
124
cls._default_model = model
125
return model
126
127
@classmethod
128
def create_messages(cls, prompt: str, image_response: ImageResponse = None):
129
if not image_response:
130
content = {"content_type": "text", "parts": [prompt]}
131
else:
132
content = {
133
"content_type": "multimodal_text",
134
"parts": [{
135
"asset_pointer": f"file-service://{image_response.get('file_id')}",
136
"height": image_response.get("height"),
137
"size_bytes": image_response.get("file_size"),
138
"width": image_response.get("width"),
139
}, prompt]
140
}
141
messages = [{
142
"id": str(uuid.uuid4()),
143
"author": {"role": "user"},
144
"content": content,
145
}]
146
if image_response:
147
messages[0]["metadata"] = {
148
"attachments": [{
149
"height": image_response.get("height"),
150
"id": image_response.get("file_id"),
151
"mimeType": image_response.get("mime_type"),
152
"name": image_response.get("file_name"),
153
"size": image_response.get("file_size"),
154
"width": image_response.get("width"),
155
}]
156
}
157
return messages
158
159
@classmethod
160
async def get_image_response(cls, session: StreamSession, headers: dict, line: dict):
161
if "parts" in line["message"]["content"]:
162
part = line["message"]["content"]["parts"][0]
163
if "asset_pointer" in part and part["metadata"]:
164
file_id = part["asset_pointer"].split("file-service://", 1)[1]
165
prompt = part["metadata"]["dalle"]["prompt"]
166
async with session.get(
167
f"{cls.url}/backend-api/files/{file_id}/download",
168
headers=headers
169
) as response:
170
response.raise_for_status()
171
download_url = (await response.json())["download_url"]
172
return ImageResponse(download_url, prompt)
66
173
67
174
@classmethod
68
175
async def create_async_generator(
@@ -78,13 +185,12 @@ class OpenaiChat(AsyncGeneratorProvider):
78
185
action: str = "next",
79
186
conversation_id: str = None,
80
187
parent_id: str = None,
188
image: ImageType = None,
81
189
response_fields: bool = False,
82
190
**kwargs
83
191
) -> AsyncResult:
84
if not model:
85
model = "gpt-3.5"
86
elif model not in models:
87
raise ValueError(f"Model are not supported: {model}")
192
if model in models:
193
model = models[model]
88
194
if not parent_id:
89
195
parent_id = str(uuid.uuid4())
90
196
if not cookies:
@@ -98,115 +204,131 @@ class OpenaiChat(AsyncGeneratorProvider):
98
204
login_url = os.environ.get("G4F_LOGIN_URL")
99
205
if login_url:
100
206
yield f"Please login: [ChatGPT]({login_url})\n\n"
101
cls._cookies["access_token"] = access_token = await cls.browse_access_token(proxy)
207
access_token, cookies = cls.browse_access_token(proxy)
208
cls._cookies = cookies
102
209
headers = {
103
"Accept": "text/event-stream",
104
210
"Authorization": f"Bearer {access_token}",
105
211
}
106
212
async with StreamSession(
107
213
proxies={"https": proxy},
108
214
impersonate="chrome110",
109
headers=headers,
110
215
timeout=timeout,
111
216
cookies=dict([(name, value) for name, value in cookies.items() if name == "_puid"])
112
217
) as session:
218
if not model:
219
model = await cls.get_default_model(session, headers)
220
try:
221
image_response = None
222
if image:
223
image_response = await cls.upload_image(session, headers, image)
224
yield image_response
225
except Exception as e:
226
yield e
113
227
end_turn = EndTurn()
114
228
while not end_turn.is_end:
115
229
data = {
116
230
"action": action,
117
"arkose_token": await get_arkose_token(proxy, timeout),
231
"arkose_token": await cls.get_arkose_token(session),
118
232
"conversation_id": conversation_id,
119
233
"parent_message_id": parent_id,
120
"model": models[model],
234
"model": model,
121
235
"history_and_training_disabled": history_disabled and not auto_continue,
122
236
}
123
237
if action != "continue":
124
238
prompt = format_prompt(messages) if not conversation_id else messages[-1]["content"]
125
data["messages"] = [{
126
"id": str(uuid.uuid4()),
127
"author": {"role": "user"},
128
"content": {"content_type": "text", "parts": [prompt]},
129
}]
130
async with session.post(f"{cls.url}/backend-api/conversation", json=data) as response:
239
data["messages"] = cls.create_messages(prompt, image_response)
240
async with session.post(
241
f"{cls.url}/backend-api/conversation",
242
json=data,
243
headers={"Accept": "text/event-stream", **headers}
244
) as response:
131
245
try:
132
246
response.raise_for_status()
133
247
except:
134
raise RuntimeError(f"Error {response.status_code}: {await response.text()}")
135
last_message = 0
136
async for line in response.iter_lines():
137
if not line.startswith(b"data: "):
138
continue
139
line = line[6:]
140
if line == b"[DONE]":
141
break
142
try:
143
line = json.loads(line)
144
except:
145
continue
146
if "message" not in line:
147
continue
148
if "error" in line and line["error"]:
149
raise RuntimeError(line["error"])
150
if "message_type" not in line["message"]["metadata"]:
151
continue
152
if line["message"]["author"]["role"] != "assistant":
153
continue
154
if line["message"]["metadata"]["message_type"] in ("next", "continue", "variant"):
155
conversation_id = line["conversation_id"]
156
parent_id = line["message"]["id"]
157
if response_fields:
158
response_fields = False
159
yield ResponseFields(conversation_id, parent_id, end_turn)
160
new_message = line["message"]["content"]["parts"][0]
161
yield new_message[last_message:]
162
last_message = len(new_message)
163
if "finish_details" in line["message"]["metadata"]:
164
if line["message"]["metadata"]["finish_details"]["type"] == "stop":
165
end_turn.end()
248
raise RuntimeError(f"Response {response.status_code}: {await response.text()}")
249
try:
250
last_message: int = 0
251
async for line in response.iter_lines():
252
if not line.startswith(b"data: "):
253
continue
254
elif line.startswith(b"data: [DONE]"):
255
break
256
try:
257
line = json.loads(line[6:])
258
except:
259
continue
260
if "message" not in line:
261
continue
262
if "error" in line and line["error"]:
263
raise RuntimeError(line["error"])
264
if "message_type" not in line["message"]["metadata"]:
265
continue
266
try:
267
image_response = await cls.get_image_response(session, headers, line)
268
if image_response:
269
yield image_response
270
except Exception as e:
271
yield e
272
if line["message"]["author"]["role"] != "assistant":
273
continue
274
if line["message"]["metadata"]["message_type"] in ("next", "continue", "variant"):
275
conversation_id = line["conversation_id"]
276
parent_id = line["message"]["id"]
277
if response_fields:
278
response_fields = False
279
yield ResponseFields(conversation_id, parent_id, end_turn)
280
if "parts" in line["message"]["content"]:
281
new_message = line["message"]["content"]["parts"][0]
282
if len(new_message) > last_message:
283
yield new_message[last_message:]
284
last_message = len(new_message)
285
if "finish_details" in line["message"]["metadata"]:
286
if line["message"]["metadata"]["finish_details"]["type"] == "stop":
287
end_turn.end()
288
break
289
except Exception as e:
290
yield e
166
291
if not auto_continue:
167
292
break
168
293
action = "continue"
169
294
await asyncio.sleep(5)
295
if history_disabled:
296
async with session.patch(
297
f"{cls.url}/backend-api/conversation/{conversation_id}",
298
json={"is_visible": False},
299
headers=headers
300
) as response:
301
response.raise_for_status()
170
302
171
303
@classmethod
172
async def browse_access_token(cls, proxy: str = None) -> str:
173
def browse() -> str:
174
driver = get_browser(proxy=proxy)
175
try:
176
driver.get(f"{cls.url}/")
177
WebDriverWait(driver, 1200).until(
178
EC.presence_of_element_located((By.ID, "prompt-textarea"))
179
)
180
javascript = """
304
def browse_access_token(cls, proxy: str = None) -> tuple[str, dict]:
305
driver = get_browser(proxy=proxy)
306
try:
307
driver.get(f"{cls.url}/")
308
WebDriverWait(driver, 1200).until(
309
EC.presence_of_element_located((By.ID, "prompt-textarea"))
310
)
311
javascript = """
181
312
access_token = (await (await fetch('/api/auth/session')).json())['accessToken'];
182
313
expires = new Date(); expires.setTime(expires.getTime() + 60 * 60 * 24 * 7); // One week
183
314
document.cookie = 'access_token=' + access_token + ';expires=' + expires.toUTCString() + ';path=/';
184
315
return access_token;
185
316
"""
186
return driver.execute_script(javascript)
187
finally:
188
driver.quit()
189
loop = get_event_loop()
190
return await loop.run_in_executor(
191
None,
192
browse
193
)
194
195
async def get_arkose_token(proxy: str = None, timeout: int = None) -> str:
196
config = {
197
"pkey": "3D86FBBA-9D22-402A-B512-3420086BA6CC",
198
"surl": "https://tcr9i.chat.openai.com",
199
"headers": {
200
"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'
201
},
202
"site": "https://chat.openai.com",
203
}
204
args_for_request = get_values_for_request(config)
205
async with StreamSession(
206
proxies={"https": proxy},
207
impersonate="chrome107",
208
timeout=timeout
209
) as session:
317
return driver.execute_script(javascript), get_driver_cookies(driver)
318
finally:
319
driver.quit()
320
321
@classmethod
322
async def get_arkose_token(cls, session: StreamSession) -> str:
323
config = {
324
"pkey": "3D86FBBA-9D22-402A-B512-3420086BA6CC",
325
"surl": "https://tcr9i.chat.openai.com",
326
"headers": {
327
"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'
328
},
329
"site": cls.url,
330
}
331
args_for_request = get_values_for_request(config)
210
332
async with session.post(**args_for_request) as response:
211
333
response.raise_for_status()
212
334
decoded_json = await response.json()
@@ -236,23 +358,47 @@ class Response():
236
358
def __init__(
237
359
self,
238
360
generator: AsyncResult,
239
fields: ResponseFields,
240
361
action: str,
241
362
messages: Messages,
242
363
options: dict
243
364
):
244
self.aiter, self.copy = tee(generator)
245
self.fields = fields
246
self.action = action
365
self._generator = generator
366
self.action: str = action
367
self.is_end: bool = False
368
self._message = None
247
369
self._messages = messages
248
370
self._options = options
371
self._fields = None
372
373
async def generator(self):
374
if self._generator:
375
self._generator = None
376
chunks = []
377
async for chunk in self._generator:
378
if isinstance(chunk, ResponseFields):
379
self._fields = chunk
380
else:
381
yield chunk
382
chunks.append(str(chunk))
383
self._message = "".join(chunks)
384
if not self._fields:
385
raise RuntimeError("Missing response fields")
386
self.is_end = self._fields._end_turn.is_end
249
387
250
388
def __aiter__(self):
251
return self.aiter
389
return self.generator()
252
390
253
391
@async_cached_property
254
392
async def message(self) -> str:
255
return "".join([chunk async for chunk in self.copy])
393
[_ async for _ in self.generator()]
394
return self._message
395
396
async def get_fields(self):
397
[_ async for _ in self.generator()]
398
return {
399
"conversation_id": self._fields.conversation_id,
400
"parent_id": self._fields.message_id,
401
}
256
402
257
403
async def next(self, prompt: str, **kwargs) -> Response:
258
404
return await OpenaiChat.create(
@@ -260,20 +406,19 @@ class Response():
260
406
prompt=prompt,
261
407
messages=await self.messages,
262
408
action="next",
263
conversation_id=self.fields.conversation_id,
264
parent_id=self.fields.message_id,
409
**await self.get_fields(),
265
410
**kwargs
266
411
)
267
412
268
413
async def do_continue(self, **kwargs) -> Response:
269
if self.end_turn:
414
fields = await self.get_fields()
415
if self.is_end:
270
416
raise RuntimeError("Can't continue message. Message already finished.")
271
417
return await OpenaiChat.create(
272
418
**self._options,
273
419
messages=await self.messages,
274
420
action="continue",
275
conversation_id=self.fields.conversation_id,
276
parent_id=self.fields.message_id,
421
**fields,
277
422
**kwargs
278
423
)
279
424
@@ -284,8 +429,7 @@ class Response():
284
429
**self._options,
285
430
messages=self._messages,
286
431
action="variant",
287
conversation_id=self.fields.conversation_id,
288
parent_id=self.fields.message_id,
432
**await self.get_fields(),
289
433
**kwargs
290
434
)
291
435
@@ -295,8 +439,4 @@ class Response():
295
439
messages.append({
296
440
"role": "assistant", "content": await self.message
297
441
})
298
return messages
299
300
@property
301
def end_turn(self):
302
return self.fields._end_turn.is_end
442
return messages
@@ -17,7 +17,7 @@ def get_model_and_provider(model : Union[Model, str],
17
17
ignore_stream: bool = False) -> tuple[str, ProviderType]:
18
18
if debug.version_check:
19
19
debug.version_check = False
20
version.utils.check_pypi_version()
20
version.utils.check_version()
21
21
22
22
if isinstance(provider, str):
23
23
if provider in ProviderUtils.convert:
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
2
2
from .typing import Messages, CreateResult, Union
3
3
4
4
class BaseProvider(ABC):
5
url: str
5
url: str = None
6
6
working: bool = False
7
7
needs_auth: bool = False
8
8
supports_stream: bool = False
@@ -217,7 +217,6 @@ body {
217
217
}
218
218
219
219
.message {
220
221
220
width: 100%;
222
221
overflow-wrap: break-word;
223
222
display: flex;
@@ -302,10 +301,14 @@ body {
302
301
line-height: 1.3;
303
302
color: var(--colour-3);
304
303
}
305
.message .content pre {
304
.message .content pre{
306
305
white-space: pre-wrap;
307
306
}
308
307
308
.message .content img{
309
max-width: 400px;
310
}
311
309
312
.message .user i {
310
313
position: absolute;
311
314
bottom: -6px;
@@ -401,13 +404,28 @@ body {
401
404
display: none;
402
405
}
403
406
404
input[type="checkbox"] {
407
#image {
408
display: none;
409
}
410
411
label[for="image"]:has(> input:valid){
412
color: var(--accent);
413
}
414
415
label[for="image"] {
416
cursor: pointer;
417
position: absolute;
418
top: 10px;
419
left: 10px;
420
}
421
422
.buttons input[type="checkbox"] {
405
423
height: 0;
406
424
width: 0;
407
425
display: none;
408
426
}
409
427
410
label {
428
.buttons label {
411
429
cursor: pointer;
412
430
text-indent: -9999px;
413
431
width: 50px;
@@ -424,7 +442,7 @@ label {
424
442
transition: 0.33s;
425
443
}
426
444
427
label:after {
445
.buttons label:after {
428
446
content: "";
429
447
position: absolute;
430
448
top: 50%;
@@ -437,11 +455,11 @@ label:after {
437
455
transition: 0.33s;
438
456
}
439
457
440
input:checked+label {
441
background: var(--blur-border);
458
.buttons input:checked+label {
459
background: var(--accent);
442
460
}
443
461
444
input:checked+label:after {
462
.buttons input:checked+label:after {
445
463
left: calc(100% - 5px - 20px);
446
464
}
447
465
@@ -36,7 +36,8 @@
36
36
37
37
#message-input {
38
38
margin-right: 30px;
39
height: 80px;
39
height: 82px;
40
margin-left: 20px;
40
41
}
41
42
42
43
#message-input::-webkit-scrollbar {
@@ -113,6 +114,10 @@
113
114
<div class="box input-box">
114
115
<textarea id="message-input" placeholder="Ask a question" cols="30" rows="10"
115
116
style="white-space: pre-wrap;resize: none;"></textarea>
117
<label for="image" title="Works only with Bing and OpenaiChat">
118
<input type="file" id="image" name="image" accept="image/png, image/gif, image/jpeg" required/>
119
<i class="fa-regular fa-image"></i>
120
</label>
116
121
<div id="send-button">
117
122
<i class="fa-solid fa-paper-plane-top"></i>
118
123
</div>
@@ -120,52 +125,7 @@
120
125
</div>
121
126
<div class="buttons">
122
127
<div class="field">
123
<input type="checkbox" id="switch" />
124
<label for="switch"></label>
125
<span class="about">Web Access</span>
126
</div>
127
<div class="field">
128
<input type="checkbox" id="patch" />
129
<label for="patch" title="Works only with Bing and some other providers"></label>
130
<span class="about">Image Generator</span>
131
</div>
132
<div class="field">
133
<select name="model" id="model">
134
<option value="gpt-3.5-turbo" selected="">gpt-3.5-turbo</option>
135
<option value="gpt-3.5-turbo-0613">gpt-3.5-turbo-0613</option>
136
<option value="gpt-3.5-turbo-16k">gpt-3.5-turbo-16k</option>
137
<option value="gpt-3.5-turbo-16k-0613">gpt-3.5-turbo-16k-0613</option>
138
<option value="gpt-4">gpt-4</option>
139
<option value="gpt-4-0613">gpt-4-0613</option>
140
<option value="gpt-4-32k">gpt-4-32k</option>
141
<option value="gpt-4-32k-0613">gpt-4-32k-0613</option>
142
<option value="palm2">palm2</option>
143
<option value="palm">palm</option>
144
<option value="google">google</option>
145
<option value="google-bard">google-bard</option>
146
<option value="google-palm">google-palm</option>
147
<option value="bard">bard</option>
148
<option value="llama2-7b">llama2-7b</option>
149
<option value="llama2-13b">llama2-13b</option>
150
<option value="llama2-70b">llama2-70b</option>
151
<option value="command-nightly">command-nightly</option>
152
<option value="gpt-neox-20b">gpt-neox-20b</option>
153
<option value="santacoder">santacoder</option>
154
<option value="bloom">bloom</option>
155
<option value="flan-t5-xxl">flan-t5-xxl</option>
156
<option value="code-davinci-002">code-davinci-002</option>
157
<option value="text-ada-001">text-ada-001</option>
158
<option value="text-babbage-001">text-babbage-001</option>
159
<option value="text-curie-001">text-curie-001</option>
160
<option value="text-davinci-002">text-davinci-002</option>
161
<option value="text-davinci-003">text-davinci-003</option>
162
<option value="llama70b-v2-chat">llama70b-v2-chat</option>
163
<option value="llama13b-v2-chat">llama13b-v2-chat</option>
164
<option value="llama7b-v2-chat">llama7b-v2-chat</option>
165
<option value="oasst-sft-1-pythia-12b">oasst-sft-1-pythia-12b</option>
166
<option value="oasst-sft-4-pythia-12b-epoch-3.5">oasst-sft-4-pythia-12b-epoch-3.5</option>
167
<option value="command-light-nightly">command-light-nightly</option>
168
</select>
128
<select name="model" id="model"></select>
169
129
</div>
170
130
<div class="field">
171
131
<select name="jailbreak" id="jailbreak" style="display: none;">
@@ -178,36 +138,19 @@
178
138
<option value="gpt-evil-1.0">evil 1.0</option>
179
139
</select>
180
140
<div class="field">
181
<select name="provider" id="provider">
182
<option value="g4f.Provider.Auto" selected>Set Provider</option>
183
<option value="g4f.Provider.AItianhuSpace">AItianhuSpace</option>
184
<option value="g4f.Provider.ChatgptLogin">ChatgptLogin</option>
185
<option value="g4f.Provider.ChatgptDemo">ChatgptDemo</option>
186
<option value="g4f.Provider.ChatgptDuo">ChatgptDuo</option>
187
<option value="g4f.Provider.Vitalentum">Vitalentum</option>
188
<option value="g4f.Provider.ChatgptAi">ChatgptAi</option>
189
<option value="g4f.Provider.AItianhu">AItianhu</option>
190
<option value="g4f.Provider.ChatBase">ChatBase</option>
191
<option value="g4f.Provider.Liaobots">Liaobots</option>
192
<option value="g4f.Provider.Yqcloud">Yqcloud</option>
193
<option value="g4f.Provider.Myshell">Myshell</option>
194
<option value="g4f.Provider.FreeGpt">FreeGpt</option>
195
<option value="g4f.Provider.Vercel">Vercel</option>
196
<option value="g4f.Provider.Aichat">Aichat</option>
197
<option value="g4f.Provider.GPTalk">GPTalk</option>
198
<option value="g4f.Provider.GptGod">GptGod</option>
199
<option value="g4f.Provider.AiAsk">AiAsk</option>
200
<option value="g4f.Provider.GptGo">GptGo</option>
201
<option value="g4f.Provider.Ylokh">Ylokh</option>
202
<option value="g4f.Provider.Bard">Bard</option>
203
<option value="g4f.Provider.Aibn">Aibn</option>
204
<option value="g4f.Provider.Bing">Bing</option>
205
<option value="g4f.Provider.You">You</option>
206
<option value="g4f.Provider.Llama2">Llama2</option>
207
<option value="g4f.Provider.Aivvm">Aivvm</option>
208
</select>
141
<select name="provider" id="provider"></select>
209
142
</div>
210
143
</div>
144
<div class="field">
145
<input type="checkbox" id="switch" />
146
<label for="switch"></label>
147
<span class="about">Web Access</span>
148
</div>
149
<div class="field">
150
<input type="checkbox" id="patch" />
151
<label for="patch" title="Works only with Bing and some other providers"></label>
152
<span class="about">Image Generator</span>
153
</div>
211
154
</div>
212
155
</div>
213
156
</div>