返回提交历史
Modified
g4f/Provider/Bing.py
+126
-104
Modified
g4f/Provider/FreeChatgpt.py
+10
-5
Modified
g4f/Provider/base_provider.py
+62
-65
Modified
g4f/Provider/bing/conversation.py
+42
-2
Modified
g4f/Provider/bing/create_images.py
+157
-67
Modified
g4f/Provider/bing/upload_image.py
+128
-60
Modified
g4f/Provider/helper.py
+83
-60
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+229
-110
Modified
g4f/Provider/retry_provider.py
+49
-9
Modified
g4f/__init__.py
+88
-3
Modified
g4f/base_provider.py
+71
-10
Modified
g4f/gui/client/css/style.css
+11
-2
Modified
g4f/gui/client/html/index.html
+22
-2
Modified
g4f/gui/client/js/chat.v1.js
+53
-21
Modified
g4f/image.py
+100
-5
Modified
g4f/models.py
+15
-0
Modified
g4f/requests.py
+42
-6
Modified
g4f/version.py
+59
-10
Modified
g4f/webdriver.py
+53
-23
XFEstudio/gpt4free
Refactor code with AI Add doctypes to many functions Add file upload for text files Add alternative url to FreeChatgpt Add webp to allowed image types
5756586c
代码差异
19 个文件
+1400
-564
@@ -15,12 +15,18 @@ from .bing.upload_image import upload_image
15
15
from .bing.create_images import create_images
16
16
from .bing.conversation import Conversation, create_conversation, delete_conversation
17
17
18
class Tones():
18
class Tones:
19
"""
20
Defines the different tone options for the Bing provider.
21
"""
19
22
creative = "Creative"
20
23
balanced = "Balanced"
21
24
precise = "Precise"
22
25
23
26
class Bing(AsyncGeneratorProvider):
27
"""
28
Bing provider for generating responses using the Bing API.
29
"""
24
30
url = "https://bing.com/chat"
25
31
working = True
26
32
supports_message_history = True
@@ -38,6 +44,19 @@ class Bing(AsyncGeneratorProvider):
38
44
web_search: bool = False,
39
45
**kwargs
40
46
) -> AsyncResult:
47
"""
48
Creates an asynchronous generator for producing responses from Bing.
49
50
:param model: The model to use.
51
:param messages: Messages to process.
52
:param proxy: Proxy to use for requests.
53
:param timeout: Timeout for requests.
54
:param cookies: Cookies for the session.
55
:param tone: The tone of the response.
56
:param image: The image type to be used.
57
:param web_search: Flag to enable or disable web search.
58
:return: An asynchronous result object.
59
"""
41
60
if len(messages) < 2:
42
61
prompt = messages[0]["content"]
43
62
context = None
@@ -56,65 +75,48 @@ class Bing(AsyncGeneratorProvider):
56
75
57
76
return stream_generate(prompt, tone, image, context, proxy, cookies, web_search, gpt4_turbo, timeout)
58
77
59
def create_context(messages: Messages):
78
def create_context(messages: Messages) -> str:
79
"""
80
Creates a context string from a list of messages.
81
82
:param messages: A list of message dictionaries.
83
:return: A string representing the context created from the messages.
84
"""
60
85
return "".join(
61
f"[{message['role']}]" + ("(#message)" if message['role']!="system" else "(#additional_instructions)") + f"\n{message['content']}\n\n"
86
f"[{message['role']}]" + ("(#message)" if message['role'] != "system" else "(#additional_instructions)") + f"\n{message['content']}\n\n"
62
87
for message in messages
63
88
)
64
89
65
90
class Defaults:
91
"""
92
Default settings and configurations for the Bing provider.
93
"""
66
94
delimiter = "\x1e"
67
95
ip_address = f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}"
68
96
97
# List of allowed message types for Bing responses
69
98
allowedMessageTypes = [
70
"ActionRequest",
71
"Chat",
72
"Context",
73
# "Disengaged", unwanted
74
"Progress",
75
# "AdsQuery", unwanted
76
"SemanticSerp",
77
"GenerateContentQuery",
78
"SearchQuery",
79
# The following message types should not be added so that it does not flood with
80
# useless messages (such as "Analyzing images" or "Searching the web") while it's retrieving the AI response
81
# "InternalSearchQuery",
82
# "InternalSearchResult",
83
"RenderCardRequest",
84
# "RenderContentRequest"
99
"ActionRequest", "Chat", "Context", "Progress", "SemanticSerp",
100
"GenerateContentQuery", "SearchQuery", "RenderCardRequest"
85
101
]
86
102
87
103
sliceIds = [
88
'abv2',
89
'srdicton',
90
'convcssclick',
91
'stylewv2',
92
'contctxp2tf',
93
'802fluxv1pc_a',
94
'806log2sphs0',
95
'727savemem',
96
'277teditgnds0',
97
'207hlthgrds0',
104
'abv2', 'srdicton', 'convcssclick', 'stylewv2', 'contctxp2tf',
105
'802fluxv1pc_a', '806log2sphs0', '727savemem', '277teditgnds0', '207hlthgrds0'
98
106
]
99
107
108
# Default location settings
100
109
location = {
101
"locale": "en-US",
102
"market": "en-US",
103
"region": "US",
104
"locationHints": [
105
{
106
"country": "United States",
107
"state": "California",
108
"city": "Los Angeles",
109
"timezoneoffset": 8,
110
"countryConfidence": 8,
111
"Center": {"Latitude": 34.0536909, "Longitude": -118.242766},
112
"RegionType": 2,
113
"SourceType": 1,
114
}
115
],
110
"locale": "en-US", "market": "en-US", "region": "US",
111
"locationHints": [{
112
"country": "United States", "state": "California", "city": "Los Angeles",
113
"timezoneoffset": 8, "countryConfidence": 8,
114
"Center": {"Latitude": 34.0536909, "Longitude": -118.242766},
115
"RegionType": 2, "SourceType": 1
116
}],
116
117
}
117
118
119
# Default headers for requests
118
120
headers = {
119
121
'accept': '*/*',
120
122
'accept-language': 'en-US,en;q=0.9',
@@ -139,23 +141,13 @@ class Defaults:
139
141
}
140
142
141
143
optionsSets = [
142
'nlu_direct_response_filter',
143
'deepleo',
144
'disable_emoji_spoken_text',
145
'responsible_ai_policy_235',
146
'enablemm',
147
'iyxapbing',
148
'iycapbing',
149
'gencontentv3',
150
'fluxsrtrunc',
151
'fluxtrunc',
152
'fluxv1',
153
'rai278',
154
'replaceurl',
155
'eredirecturl',
156
'nojbfedge'
144
'nlu_direct_response_filter', 'deepleo', 'disable_emoji_spoken_text',
145
'responsible_ai_policy_235', 'enablemm', 'iyxapbing', 'iycapbing',
146
'gencontentv3', 'fluxsrtrunc', 'fluxtrunc', 'fluxv1', 'rai278',
147
'replaceurl', 'eredirecturl', 'nojbfedge'
157
148
]
158
149
150
# Default cookies
159
151
cookies = {
160
152
'SRCHD' : 'AF=NOFORM',
161
153
'PPLState' : '1',
@@ -166,6 +158,12 @@ class Defaults:
166
158
}
167
159
168
160
def format_message(msg: dict) -> str:
161
"""
162
Formats a message dictionary into a JSON string with a delimiter.
163
164
:param msg: The message dictionary to format.
165
:return: A formatted string representation of the message.
166
"""
169
167
return json.dumps(msg, ensure_ascii=False) + Defaults.delimiter
170
168
171
169
def create_message(
@@ -177,7 +175,20 @@ def create_message(
177
175
web_search: bool = False,
178
176
gpt4_turbo: bool = False
179
177
) -> str:
178
"""
179
Creates a message for the Bing API with specified parameters.
180
181
:param conversation: The current conversation object.
182
:param prompt: The user's input prompt.
183
:param tone: The desired tone for the response.
184
:param context: Additional context for the prompt.
185
:param image_response: The response if an image is involved.
186
:param web_search: Flag to enable web search.
187
:param gpt4_turbo: Flag to enable GPT-4 Turbo.
188
:return: A formatted string message for the Bing API.
189
"""
180
190
options_sets = Defaults.optionsSets
191
# Append tone-specific options
181
192
if tone == Tones.creative:
182
193
options_sets.append("h3imaginative")
183
194
elif tone == Tones.precise:
@@ -186,54 +197,49 @@ def create_message(
186
197
options_sets.append("galileo")
187
198
else:
188
199
options_sets.append("harmonyv3")
189
200
201
# Additional configurations based on parameters
190
202
if not web_search:
191
203
options_sets.append("nosearchall")
192
193
204
if gpt4_turbo:
194
205
options_sets.append("dlgpt4t")
195
206
196
207
request_id = str(uuid.uuid4())
197
208
struct = {
198
'arguments': [
199
{
200
'source': 'cib',
201
'optionsSets': options_sets,
202
'allowedMessageTypes': Defaults.allowedMessageTypes,
203
'sliceIds': Defaults.sliceIds,
204
'traceId': os.urandom(16).hex(),
205
'isStartOfSession': True,
209
'arguments': [{
210
'source': 'cib', 'optionsSets': options_sets,
211
'allowedMessageTypes': Defaults.allowedMessageTypes,
212
'sliceIds': Defaults.sliceIds,
213
'traceId': os.urandom(16).hex(), 'isStartOfSession': True,
214
'requestId': request_id,
215
'message': {
216
**Defaults.location,
217
'author': 'user',
218
'inputMethod': 'Keyboard',
219
'text': prompt,
220
'messageType': 'Chat',
206
221
'requestId': request_id,
207
'message': {**Defaults.location, **{
208
'author': 'user',
209
'inputMethod': 'Keyboard',
210
'text': prompt,
211
'messageType': 'Chat',
212
'requestId': request_id,
213
'messageId': request_id,
214
}},
215
"verbosity": "verbose",
216
"scenario": "SERP",
217
"plugins":[
218
{"id":"c310c353-b9f0-4d76-ab0d-1dd5e979cf68", "category": 1}
219
] if web_search else [],
220
'tone': tone,
221
'spokenTextMode': 'None',
222
'conversationId': conversation.conversationId,
223
'participant': {
224
'id': conversation.clientId
225
},
226
}
227
],
222
'messageId': request_id
223
},
224
"verbosity": "verbose",
225
"scenario": "SERP",
226
"plugins": [{"id": "c310c353-b9f0-4d76-ab0d-1dd5e979cf68", "category": 1}] if web_search else [],
227
'tone': tone,
228
'spokenTextMode': 'None',
229
'conversationId': conversation.conversationId,
230
'participant': {'id': conversation.clientId},
231
}],
228
232
'invocationId': '1',
229
233
'target': 'chat',
230
234
'type': 4
231
235
}
232
if image_response.get('imageUrl') and image_response.get('originalImageUrl'):
236
237
if image_response and image_response.get('imageUrl') and image_response.get('originalImageUrl'):
233
238
struct['arguments'][0]['message']['originalImageUrl'] = image_response.get('originalImageUrl')
234
239
struct['arguments'][0]['message']['imageUrl'] = image_response.get('imageUrl')
235
240
struct['arguments'][0]['experienceType'] = None
236
241
struct['arguments'][0]['attachedFileInfo'] = {"fileName": None, "fileType": None}
242
237
243
if context:
238
244
struct['arguments'][0]['previousMessages'] = [{
239
245
"author": "user",
@@ -242,30 +248,46 @@ def create_message(
242
248
"messageType": "Context",
243
249
"messageId": "discover-web--page-ping-mriduna-----"
244
250
}]
251
245
252
return format_message(struct)
246
253
247
254
async def stream_generate(
248
prompt: str,
249
tone: str,
250
image: ImageType = None,
251
context: str = None,
252
proxy: str = None,
253
cookies: dict = None,
254
web_search: bool = False,
255
gpt4_turbo: bool = False,
256
timeout: int = 900
257
):
255
prompt: str,
256
tone: str,
257
image: ImageType = None,
258
context: str = None,
259
proxy: str = None,
260
cookies: dict = None,
261
web_search: bool = False,
262
gpt4_turbo: bool = False,
263
timeout: int = 900
264
):
265
"""
266
Asynchronously streams generated responses from the Bing API.
267
268
:param prompt: The user's input prompt.
269
:param tone: The desired tone for the response.
270
:param image: The image type involved in the response.
271
:param context: Additional context for the prompt.
272
:param proxy: Proxy settings for the request.
273
:param cookies: Cookies for the session.
274
:param web_search: Flag to enable web search.
275
:param gpt4_turbo: Flag to enable GPT-4 Turbo.
276
:param timeout: Timeout for the request.
277
:return: An asynchronous generator yielding responses.
278
"""
258
279
headers = Defaults.headers
259
280
if cookies:
260
281
headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
282
261
283
async with ClientSession(
262
timeout=ClientTimeout(total=timeout),
263
headers=headers
284
timeout=ClientTimeout(total=timeout), headers=headers
264
285
) as session:
265
286
conversation = await create_conversation(session, proxy)
266
287
image_response = await upload_image(session, image, tone, proxy) if image else None
267
288
if image_response:
268
289
yield image_response
290
269
291
try:
270
292
async with session.ws_connect(
271
293
'wss://sydney.bing.com/sydney/ChatHub',
@@ -289,7 +311,7 @@ async def stream_generate(
289
311
if obj is None or not obj:
290
312
continue
291
313
response = json.loads(obj)
292
if response.get('type') == 1 and response['arguments'][0].get('messages'):
314
if response and response.get('type') == 1 and response['arguments'][0].get('messages'):
293
315
message = response['arguments'][0]['messages'][0]
294
316
image_response = None
295
317
if (message['contentOrigin'] != 'Apology'):
@@ -1,16 +1,20 @@
1
1
from __future__ import annotations
2
2
3
import json
3
import json, random
4
4
from aiohttp import ClientSession
5
5
6
6
from ..typing import AsyncResult, Messages
7
7
from .base_provider import AsyncGeneratorProvider
8
8
9
10
9
models = {
11
"claude-v2": "claude-2.0",
12
"gemini-pro": "google-gemini-pro"
10
"claude-v2": "claude-2.0",
11
"claude-v2.1":"claude-2.1",
12
"gemini-pro": "google-gemini-pro"
13
13
}
14
urls = [
15
"https://free.chatgpt.org.uk",
16
"https://ai.chatgpt.org.uk"
17
]
14
18
15
19
class FreeChatgpt(AsyncGeneratorProvider):
16
20
url = "https://free.chatgpt.org.uk"
@@ -31,6 +35,7 @@ class FreeChatgpt(AsyncGeneratorProvider):
31
35
model = models[model]
32
36
elif not model:
33
37
model = "gpt-3.5-turbo"
38
url = random.choice(urls)
34
39
headers = {
35
40
"Accept": "application/json, text/event-stream",
36
41
"Content-Type":"application/json",
@@ -55,7 +60,7 @@ class FreeChatgpt(AsyncGeneratorProvider):
55
60
"top_p":1,
56
61
**kwargs
57
62
}
58
async with session.post(f'{cls.url}/api/openai/v1/chat/completions', json=data, proxy=proxy) as response:
63
async with session.post(f'{url}/api/openai/v1/chat/completions', json=data, proxy=proxy) as response:
59
64
response.raise_for_status()
60
65
started = False
61
66
async for line in response.content:
@@ -1,28 +1,29 @@
1
1
from __future__ import annotations
2
3
2
import sys
4
3
import asyncio
5
from asyncio import AbstractEventLoop
4
from asyncio import AbstractEventLoop
6
5
from concurrent.futures import ThreadPoolExecutor
7
from abc import abstractmethod
8
from inspect import signature, Parameter
9
from .helper import get_event_loop, get_cookies, format_prompt
10
from ..typing import CreateResult, AsyncResult, Messages
11
from ..base_provider import BaseProvider
6
from abc import abstractmethod
7
from inspect import signature, Parameter
8
from .helper import get_event_loop, get_cookies, format_prompt
9
from ..typing import CreateResult, AsyncResult, Messages
10
from ..base_provider import BaseProvider
12
11
13
12
if sys.version_info < (3, 10):
14
13
NoneType = type(None)
15
14
else:
16
15
from types import NoneType
17
16
18
# Change event loop policy on windows for curl_cffi
17
# Set Windows event loop policy for better compatibility with asyncio and curl_cffi
19
18
if sys.platform == 'win32':
20
if isinstance(
21
asyncio.get_event_loop_policy(), asyncio.WindowsProactorEventLoopPolicy
22
):
19
if isinstance(asyncio.get_event_loop_policy(), asyncio.WindowsProactorEventLoopPolicy):
23
20
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
24
21
25
22
class AbstractProvider(BaseProvider):
23
"""
24
Abstract class for providing asynchronous functionality to derived classes.
25
"""
26
26
27
@classmethod
27
28
async def create_async(
28
29
cls,
@@ -33,62 +34,50 @@ class AbstractProvider(BaseProvider):
33
34
executor: ThreadPoolExecutor = None,
34
35
**kwargs
35
36
) -> str:
36
if not loop:
37
loop = get_event_loop()
37
"""
38
Asynchronously creates a result based on the given model and messages.
39
"""
40
loop = loop or get_event_loop()
38
41
39
42
def create_func() -> str:
40
return "".join(cls.create_completion(
41
model,
42
messages,
43
False,
44
**kwargs
45
))
43
return "".join(cls.create_completion(model, messages, False, **kwargs))
46
44
47
45
return await asyncio.wait_for(
48
loop.run_in_executor(
49
executor,
50
create_func
51
),
46
loop.run_in_executor(executor, create_func),
52
47
timeout=kwargs.get("timeout", 0)
53
48
)
54
49
55
50
@classmethod
56
51
@property
57
52
def params(cls) -> str:
58
if issubclass(cls, AsyncGeneratorProvider):
59
sig = signature(cls.create_async_generator)
60
elif issubclass(cls, AsyncProvider):
61
sig = signature(cls.create_async)
62
else:
63
sig = signature(cls.create_completion)
53
"""
54
Returns the parameters supported by the provider.
55
"""
56
sig = signature(
57
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
58
cls.create_async if issubclass(cls, AsyncProvider) else
59
cls.create_completion
60
)
64
61
65
62
def get_type_name(annotation: type) -> str:
66
if hasattr(annotation, "__name__"):
67
annotation = annotation.__name__
68
elif isinstance(annotation, NoneType):
69
annotation = "None"
70
return str(annotation)
71
63
return annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
64
72
65
args = ""
73
66
for name, param in sig.parameters.items():
74
if name in ("self", "kwargs"):
67
if name in ("self", "kwargs") or (name == "stream" and not cls.supports_stream):
75
68
continue
76
if name == "stream" and not cls.supports_stream:
77
continue
78
if args:
79
args += ", "
80
args += "\n " + name
81
if name != "model" and param.annotation is not Parameter.empty:
82
args += f": {get_type_name(param.annotation)}"
83
if param.default == "":
84
args += ' = ""'
85
elif param.default is not Parameter.empty:
86
args += f" = {param.default}"
69
args += f"\n {name}"
70
args += f": {get_type_name(param.annotation)}" if param.annotation is not Parameter.empty else ""
71
args += f' = "{param.default}"' if param.default == "" else f" = {param.default}" if param.default is not Parameter.empty else ""
87
72
88
73
return f"g4f.Provider.{cls.__name__} supports: ({args}\n)"
89
74
90
75
91
76
class AsyncProvider(AbstractProvider):
77
"""
78
Provides asynchronous functionality for creating completions.
79
"""
80
92
81
@classmethod
93
82
def create_completion(
94
83
cls,
@@ -99,8 +88,10 @@ class AsyncProvider(AbstractProvider):
99
88
loop: AbstractEventLoop = None,
100
89
**kwargs
101
90
) -> CreateResult:
102
if not loop:
103
loop = get_event_loop()
91
"""
92
Creates a completion result synchronously.
93
"""
94
loop = loop or get_event_loop()
104
95
coro = cls.create_async(model, messages, **kwargs)
105
96
yield loop.run_until_complete(coro)
106
97
@@ -111,10 +102,16 @@ class AsyncProvider(AbstractProvider):
111
102
messages: Messages,
112
103
**kwargs
113
104
) -> str:
105
"""
106
Abstract method for creating asynchronous results.
107
"""
114
108
raise NotImplementedError()
115
109
116
110
117
111
class AsyncGeneratorProvider(AsyncProvider):
112
"""
113
Provides asynchronous generator functionality for streaming results.
114
"""
118
115
supports_stream = True
119
116
120
117
@classmethod
@@ -127,15 +124,13 @@ class AsyncGeneratorProvider(AsyncProvider):
127
124
loop: AbstractEventLoop = None,
128
125
**kwargs
129
126
) -> CreateResult:
130
if not loop:
131
loop = get_event_loop()
132
generator = cls.create_async_generator(
133
model,
134
messages,
135
stream=stream,
136
**kwargs
137
)
127
"""
128
Creates a streaming completion result synchronously.
129
"""
130
loop = loop or get_event_loop()
131
generator = cls.create_async_generator(model, messages, stream=stream, **kwargs)
138
132
gen = generator.__aiter__()
133
139
134
while True:
140
135
try:
141
136
yield loop.run_until_complete(gen.__anext__())
@@ -149,21 +144,23 @@ class AsyncGeneratorProvider(AsyncProvider):
149
144
messages: Messages,
150
145
**kwargs
151
146
) -> str:
147
"""
148
Asynchronously creates a result from a generator.
149
"""
152
150
return "".join([
153
chunk async for chunk in cls.create_async_generator(
154
model,
155
messages,
156
stream=False,
157
**kwargs
158
) if not isinstance(chunk, Exception)
151
chunk async for chunk in cls.create_async_generator(model, messages, stream=False, **kwargs)
152
if not isinstance(chunk, Exception)
159
153
])
160
154
161
155
@staticmethod
162
156
@abstractmethod
163
def create_async_generator(
157
async def create_async_generator(
164
158
model: str,
165
159
messages: Messages,
166
160
stream: bool = True,
167
161
**kwargs
168
162
) -> AsyncResult:
169
raise NotImplementedError()
163
"""
164
Abstract method for creating an asynchronous generator.
165
"""
166
raise NotImplementedError()
@@ -1,13 +1,33 @@
1
1
from aiohttp import ClientSession
2
2
3
4
class Conversation():
3
class Conversation:
4
"""
5
Represents a conversation with specific attributes.
6
"""
5
7
def __init__(self, conversationId: str, clientId: str, conversationSignature: str) -> None:
8
"""
9
Initialize a new conversation instance.
10
11
Args:
12
conversationId (str): Unique identifier for the conversation.
13
clientId (str): Client identifier.
14
conversationSignature (str): Signature for the conversation.
15
"""
6
16
self.conversationId = conversationId
7
17
self.clientId = clientId
8
18
self.conversationSignature = conversationSignature
9
19
10
20
async def create_conversation(session: ClientSession, proxy: str = None) -> Conversation:
21
"""
22
Create a new conversation asynchronously.
23
24
Args:
25
session (ClientSession): An instance of aiohttp's ClientSession.
26
proxy (str, optional): Proxy URL. Defaults to None.
27
28
Returns:
29
Conversation: An instance representing the created conversation.
30
"""
11
31
url = 'https://www.bing.com/turing/conversation/create?bundleVersion=1.1199.4'
12
32
async with session.get(url, proxy=proxy) as response:
13
33
try:
@@ -24,12 +44,32 @@ async def create_conversation(session: ClientSession, proxy: str = None) -> Conv
24
44
return Conversation(conversationId, clientId, conversationSignature)
25
45
26
46
async def list_conversations(session: ClientSession) -> list:
47
"""
48
List all conversations asynchronously.
49
50
Args:
51
session (ClientSession): An instance of aiohttp's ClientSession.
52
53
Returns:
54
list: A list of conversations.
55
"""
27
56
url = "https://www.bing.com/turing/conversation/chats"
28
57
async with session.get(url) as response:
29
58
response = await response.json()
30
59
return response["chats"]
31
60
32
61
async def delete_conversation(session: ClientSession, conversation: Conversation, proxy: str = None) -> bool:
62
"""
63
Delete a conversation asynchronously.
64
65
Args:
66
session (ClientSession): An instance of aiohttp's ClientSession.
67
conversation (Conversation): The conversation to delete.
68
proxy (str, optional): Proxy URL. Defaults to None.
69
70
Returns:
71
bool: True if deletion was successful, False otherwise.
72
"""
33
73
url = "https://sydney.bing.com/sydney/DeleteSingleConversation"
34
74
json = {
35
75
"conversationId": conversation.conversationId,
@@ -1,9 +1,16 @@
1
"""
2
This module provides functionalities for creating and managing images using Bing's service.
3
It includes functions for user login, session creation, image creation, and processing.
4
"""
5
1
6
import asyncio
2
import time, json, os
7
import time
8
import json
9
import os
3
10
from aiohttp import ClientSession
4
11
from bs4 import BeautifulSoup
5
12
from urllib.parse import quote
6
from typing import Generator
13
from typing import Generator, List, Dict
7
14
8
15
from ..create_images import CreateImagesProvider
9
16
from ..helper import get_cookies, get_event_loop
@@ -12,23 +19,47 @@ from ...base_provider import ProviderType
12
19
from ...image import format_images_markdown
13
20
14
21
BING_URL = "https://www.bing.com"
22
TIMEOUT_LOGIN = 1200
23
TIMEOUT_IMAGE_CREATION = 300
24
ERRORS = [
25
"this prompt is being reviewed",
26
"this prompt has been blocked",
27
"we're working hard to offer image creator in more languages",
28
"we can't create your images right now"
29
]
30
BAD_IMAGES = [
31
"https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png",
32
"https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg",
33
]
34
35
def wait_for_login(driver: WebDriver, timeout: int = TIMEOUT_LOGIN) -> None:
36
"""
37
Waits for the user to log in within a given timeout period.
15
38
16
def wait_for_login(driver: WebDriver, timeout: int = 1200) -> None:
39
Args:
40
driver (WebDriver): Webdriver for browser automation.
41
timeout (int): Maximum waiting time in seconds.
42
43
Raises:
44
RuntimeError: If the login process exceeds the timeout.
45
"""
17
46
driver.get(f"{BING_URL}/")
18
value = driver.get_cookie("_U")
19
if value:
20
return
21
47
start_time = time.time()
22
while True:
48
while not driver.get_cookie("_U"):
23
49
if time.time() - start_time > timeout:
24
50
raise RuntimeError("Timeout error")
25
value = driver.get_cookie("_U")
26
if value:
27
time.sleep(1)
28
return
29
51
time.sleep(0.5)
30
52
31
def create_session(cookies: dict) -> ClientSession:
53
def create_session(cookies: Dict[str, str]) -> ClientSession:
54
"""
55
Creates a new client session with specified cookies and headers.
56
57
Args:
58
cookies (Dict[str, str]): Cookies to be used for the session.
59
60
Returns:
61
ClientSession: The created client session.
62
"""
32
63
headers = {
33
64
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
34
65
"accept-encoding": "gzip, deflate, br",
@@ -47,28 +78,32 @@ def create_session(cookies: dict) -> ClientSession:
47
78
"upgrade-insecure-requests": "1",
48
79
}
49
80
if cookies:
50
headers["cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
81
headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
51
82
return ClientSession(headers=headers)
52
83
53
async def create_images(session: ClientSession, prompt: str, proxy: str = None, timeout: int = 300) -> list:
54
url_encoded_prompt = quote(prompt)
84
async def create_images(session: ClientSession, prompt: str, proxy: str = None, timeout: int = TIMEOUT_IMAGE_CREATION) -> List[str]:
85
"""
86
Creates images based on a given prompt using Bing's service.
87
88
Args:
89
session (ClientSession): Active client session.
90
prompt (str): Prompt to generate images.
91
proxy (str, optional): Proxy configuration.
92
timeout (int): Timeout for the request.
93
94
Returns:
95
List[str]: A list of URLs to the created images.
96
97
Raises:
98
RuntimeError: If image creation fails or times out.
99
"""
100
url_encoded_prompt = quote(prompt)
55
101
payload = f"q={url_encoded_prompt}&rt=4&FORM=GENCRE"
56
102
url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=4&FORM=GENCRE"
57
async with session.post(
58
url,
59
allow_redirects=False,
60
data=payload,
61
timeout=timeout,
62
) as response:
103
async with session.post(url, allow_redirects=False, data=payload, timeout=timeout) as response:
63
104
response.raise_for_status()
64
errors = [
65
"this prompt is being reviewed",
66
"this prompt has been blocked",
67
"we're working hard to offer image creator in more languages",
68
"we can't create your images right now"
69
]
70
105
text = (await response.text()).lower()
71
for error in errors:
106
for error in ERRORS:
72
107
if error in text:
73
108
raise RuntimeError(f"Create images failed: {error}")
74
109
if response.status != 302:
@@ -107,54 +142,109 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
107
142
raise RuntimeError(error)
108
143
return read_images(text)
109
144
110
def read_images(text: str) -> list:
111
html_soup = BeautifulSoup(text, "html.parser")
112
tags = html_soup.find_all("img")
113
image_links = [img["src"] for img in tags if "mimg" in img["class"]]
114
images = [link.split("?w=")[0] for link in image_links]
115
bad_images = [
116
"https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png",
117
"https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg",
118
]
119
if any(im in bad_images for im in images):
145
def read_images(html_content: str) -> List[str]:
146
"""
147
Extracts image URLs from the HTML content.
148
149
Args:
150
html_content (str): HTML content containing image URLs.
151
152
Returns:
153
List[str]: A list of image URLs.
154
"""
155
soup = BeautifulSoup(html_content, "html.parser")
156
tags = soup.find_all("img", class_="mimg")
157
images = [img["src"].split("?w=")[0] for img in tags]
158
if any(im in BAD_IMAGES for im in images):
120
159
raise RuntimeError("Bad images found")
121
160
if not images:
122
161
raise RuntimeError("No images found")
123
162
return images
124
163
125
async def create_images_markdown(cookies: dict, prompt: str, proxy: str = None) -> str:
126
session = create_session(cookies)
127
try:
164
async def create_images_markdown(cookies: Dict[str, str], prompt: str, proxy: str = None) -> str:
165
"""
166
Creates markdown formatted string with images based on the prompt.
167
168
Args:
169
cookies (Dict[str, str]): Cookies to be used for the session.
170
prompt (str): Prompt to generate images.
171
proxy (str, optional): Proxy configuration.
172
173
Returns:
174
str: Markdown formatted string with images.
175
"""
176
async with create_session(cookies) as session:
128
177
images = await create_images(session, prompt, proxy)
129
178
return format_images_markdown(images, prompt)
130
finally:
131
await session.close()
132
179
133
def get_cookies_from_browser(proxy: str = None) -> dict:
134
driver = get_browser(proxy=proxy)
135
try:
180
def get_cookies_from_browser(proxy: str = None) -> Dict[str, str]:
181
"""
182
Retrieves cookies from the browser using webdriver.
183
184
Args:
185
proxy (str, optional): Proxy configuration.
186
187
Returns:
188
Dict[str, str]: Retrieved cookies.
189
"""
190
with get_browser(proxy=proxy) as driver:
136
191
wait_for_login(driver)
192
time.sleep(1)
137
193
return get_driver_cookies(driver)
138
finally:
139
driver.quit()
140
141
def create_completion(prompt: str, cookies: dict = None, proxy: str = None) -> Generator:
142
loop = get_event_loop()
143
if not cookies:
144
cookies = get_cookies(".bing.com")
145
if "_U" not in cookies:
146
login_url = os.environ.get("G4F_LOGIN_URL")
147
if login_url:
148
yield f"Please login: [Bing]({login_url})\n\n"
149
cookies = get_cookies_from_browser(proxy)
150
yield loop.run_until_complete(create_images_markdown(cookies, prompt, proxy))
151
152
async def create_async(prompt: str, cookies: dict = None, proxy: str = None) -> str:
153
if not cookies:
154
cookies = get_cookies(".bing.com")
155
if "_U" not in cookies:
156
cookies = get_cookies_from_browser(proxy)
157
return await create_images_markdown(cookies, prompt, proxy)
194
195
class CreateImagesBing:
196
"""A class for creating images using Bing."""
197
198
_cookies: Dict[str, str] = {}
199
200
@classmethod
201
def create_completion(cls, prompt: str, cookies: Dict[str, str] = None, proxy: str = None) -> Generator[str]:
202
"""
203
Generator for creating imagecompletion based on a prompt.
204
205
Args:
206
prompt (str): Prompt to generate images.
207
cookies (Dict[str, str], optional): Cookies for the session. If None, cookies are retrieved automatically.
208
proxy (str, optional): Proxy configuration.
209
210
Yields:
211
Generator[str, None, None]: The final output as markdown formatted string with images.
212
"""
213
loop = get_event_loop()
214
cookies = cookies or cls._cookies or get_cookies(".bing.com")
215
if "_U" not in cookies:
216
login_url = os.environ.get("G4F_LOGIN_URL")
217
if login_url:
218
yield f"Please login: [Bing]({login_url})\n\n"
219
cls._cookies = cookies = get_cookies_from_browser(proxy)
220
yield loop.run_until_complete(create_images_markdown(cookies, prompt, proxy))
221
222
@classmethod
223
async def create_async(cls, prompt: str, cookies: Dict[str, str] = None, proxy: str = None) -> str:
224
"""
225
Asynchronously creates a markdown formatted string with images based on the prompt.
226
227
Args:
228
prompt (str): Prompt to generate images.
229
cookies (Dict[str, str], optional): Cookies for the session. If None, cookies are retrieved automatically.
230
proxy (str, optional): Proxy configuration.
231
232
Returns:
233
str: Markdown formatted string with images.
234
"""
235
cookies = cookies or cls._cookies or get_cookies(".bing.com")
236
if "_U" not in cookies:
237
cls._cookies = cookies = get_cookies_from_browser(proxy)
238
return await create_images_markdown(cookies, prompt, proxy)
158
239
159
240
def patch_provider(provider: ProviderType) -> CreateImagesProvider:
160
return CreateImagesProvider(provider, create_completion, create_async)
241
"""
242
Patches a provider to include image creation capabilities.
243
244
Args:
245
provider (ProviderType): The provider to be patched.
246
247
Returns:
248
CreateImagesProvider: The patched provider with image creation capabilities.
249
"""
250
return CreateImagesProvider(provider, CreateImagesBing.create_completion, CreateImagesBing.create_async)
@@ -1,64 +1,107 @@
1
from __future__ import annotations
1
"""
2
Module to handle image uploading and processing for Bing AI integrations.
3
"""
2
4
5
from __future__ import annotations
3
6
import string
4
7
import random
5
8
import json
6
9
import math
7
from ...typing import ImageType
8
10
from aiohttp import ClientSession
11
from PIL import Image
12
13
from ...typing import ImageType, Tuple
9
14
from ...image import to_image, process_image, to_base64, ImageResponse
10
15
11
image_config = {
16
IMAGE_CONFIG = {
12
17
"maxImagePixels": 360000,
13
18
"imageCompressionRate": 0.7,
14
"enableFaceBlurDebug": 0,
19
"enableFaceBlurDebug": False,
15
20
}
16
21
17
22
async def upload_image(
18
session: ClientSession,
19
image: ImageType,
20
tone: str,
23
session: ClientSession,
24
image_data: ImageType,
25
tone: str,
21
26
proxy: str = None
22
27
) -> ImageResponse:
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 * math.sqrt(max_image_pixels / (width * height)))
28
new_height = int(height * math.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'
28
"""
29
Uploads an image to Bing's AI service and returns the image response.
30
31
Args:
32
session (ClientSession): The active session.
33
image_data (bytes): The image data to be uploaded.
34
tone (str): The tone of the conversation.
35
proxy (str, optional): Proxy if any. Defaults to None.
36
37
Raises:
38
RuntimeError: If the image upload fails.
39
40
Returns:
41
ImageResponse: The response from the image upload.
42
"""
43
image = to_image(image_data)
44
new_width, new_height = calculate_new_dimensions(image)
45
processed_img = process_image(image, new_width, new_height)
46
img_binary_data = to_base64(processed_img, IMAGE_CONFIG['imageCompressionRate'])
47
48
data, boundary = build_image_upload_payload(img_binary_data, tone)
49
headers = prepare_headers(session, boundary)
50
39
51
async with session.post("https://www.bing.com/images/kblob", data=data, headers=headers, proxy=proxy) as response:
40
52
if response.status != 200:
41
53
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 ImageResponse(result["imageUrl"], "", result)
59
60
def build_image_upload_api_payload(image_bin: str, tone: str):
61
payload = {
54
return parse_image_response(await response.json())
55
56
def calculate_new_dimensions(image: Image.Image) -> Tuple[int, int]:
57
"""
58
Calculates the new dimensions for the image based on the maximum allowed pixels.
59
60
Args:
61
image (Image): The PIL Image object.
62
63
Returns:
64
Tuple[int, int]: The new width and height for the image.
65
"""
66
width, height = image.size
67
max_image_pixels = IMAGE_CONFIG['maxImagePixels']
68
if max_image_pixels / (width * height) < 1:
69
scale_factor = math.sqrt(max_image_pixels / (width * height))
70
return int(width * scale_factor), int(height * scale_factor)
71
return width, height
72
73
def build_image_upload_payload(image_bin: str, tone: str) -> Tuple[str, str]:
74
"""
75
Builds the payload for image uploading.
76
77
Args:
78
image_bin (str): Base64 encoded image binary data.
79
tone (str): The tone of the conversation.
80
81
Returns:
82
Tuple[str, str]: The data and boundary for the payload.
83
"""
84
boundary = "----WebKitFormBoundary" + ''.join(random.choices(string.ascii_letters + string.digits, k=16))
85
data = f"--{boundary}\r\n" \
86
f"Content-Disposition: form-data; name=\"knowledgeRequest\"\r\n\r\n" \
87
f"{json.dumps(build_knowledge_request(tone), ensure_ascii=False)}\r\n" \
88
f"--{boundary}\r\n" \
89
f"Content-Disposition: form-data; name=\"imageBase64\"\r\n\r\n" \
90
f"{image_bin}\r\n" \
91
f"--{boundary}--\r\n"
92
return data, boundary
93
94
def build_knowledge_request(tone: str) -> dict:
95
"""
96
Builds the knowledge request payload.
97
98
Args:
99
tone (str): The tone of the conversation.
100
101
Returns:
102
dict: The knowledge request payload.
103
"""
104
return {
62
105
'invokedSkills': ["ImageById"],
63
106
'subscriptionId': "Bing.Chat.Multimodal",
64
107
'invokedSkillsRequestData': {
@@ -69,21 +112,46 @@ def build_image_upload_api_payload(image_bin: str, tone: str):
69
112
'convotone': tone
70
113
}
71
114
}
72
knowledge_request = {
73
'imageInfo': {},
74
'knowledgeRequest': payload
75
}
76
boundary="----WebKitFormBoundary" + ''.join(random.choices(string.ascii_letters + string.digits, k=16))
77
data = (
78
f'--{boundary}'
79
+ '\r\nContent-Disposition: form-data; name="knowledgeRequest"\r\n\r\n'
80
+ json.dumps(knowledge_request, ensure_ascii=False)
81
+ "\r\n--"
82
+ boundary
83
+ '\r\nContent-Disposition: form-data; name="imageBase64"\r\n\r\n'
84
+ image_bin
85
+ "\r\n--"
86
+ boundary
87
+ "--\r\n"
115
116
def prepare_headers(session: ClientSession, boundary: str) -> dict:
117
"""
118
Prepares the headers for the image upload request.
119
120
Args:
121
session (ClientSession): The active session.
122
boundary (str): The boundary string for the multipart/form-data.
123
124
Returns:
125
dict: The headers for the request.
126
"""
127
headers = session.headers.copy()
128
headers["Content-Type"] = f'multipart/form-data; boundary={boundary}'
129
headers["Referer"] = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
130
headers["Origin"] = 'https://www.bing.com'
131
return headers
132
133
def parse_image_response(response: dict) -> ImageResponse:
134
"""
135
Parses the response from the image upload.
136
137
Args:
138
response (dict): The response dictionary.
139
140
Raises:
141
RuntimeError: If parsing the image info fails.
142
143
Returns:
144
ImageResponse: The parsed image response.
145
"""
146
if not response.get('blobId'):
147
raise RuntimeError("Failed to parse image info.")
148
149
result = {'bcid': response.get('blobId', ""), 'blurredBcid': response.get('processedBlobId', "")}
150
result["imageUrl"] = f"https://www.bing.com/images/blob?bcid={result['blurredBcid'] or result['bcid']}"
151
152
result['originalImageUrl'] = (
153
f"https://www.bing.com/images/blob?bcid={result['blurredBcid']}"
154
if IMAGE_CONFIG["enableFaceBlurDebug"] else
155
f"https://www.bing.com/images/blob?bcid={result['bcid']}"
88
156
)
89
return data, boundary
157
return ImageResponse(result["imageUrl"], "", result)
@@ -1,36 +1,31 @@
1
1
from __future__ import annotations
2
2
3
3
import asyncio
4
import webbrowser
4
import os
5
5
import random
6
import string
7
6
import secrets
8
import os
9
from os import path
7
import string
10
8
from asyncio import AbstractEventLoop, BaseEventLoop
11
9
from platformdirs import user_config_dir
12
10
from browser_cookie3 import (
13
chrome,
14
chromium,
15
opera,
16
opera_gx,
17
brave,
18
edge,
19
vivaldi,
20
firefox,
21
_LinuxPasswordManager
11
chrome, chromium, opera, opera_gx,
12
brave, edge, vivaldi, firefox,
13
_LinuxPasswordManager, BrowserCookieError
22
14
)
23
24
15
from ..typing import Dict, Messages
25
16
from .. import debug
26
17
27
# Local Cookie Storage
18
# Global variable to store cookies
28
19
_cookies: Dict[str, Dict[str, str]] = {}
29
20
30
# If loop closed or not set, create new event loop.
31
# If event loop is already running, handle nested event loops.
32
# If "nest_asyncio" is installed, patch the event loop.
33
21
def get_event_loop() -> AbstractEventLoop:
22
"""
23
Get the current asyncio event loop. If the loop is closed or not set, create a new event loop.
24
If a loop is running, handle nested event loops. Patch the loop if 'nest_asyncio' is installed.
25
26
Returns:
27
AbstractEventLoop: The current or new event loop.
28
"""
34
29
try:
35
30
loop = asyncio.get_event_loop()
36
31
if isinstance(loop, BaseEventLoop):
@@ -39,61 +34,50 @@ def get_event_loop() -> AbstractEventLoop:
39
34
loop = asyncio.new_event_loop()
40
35
asyncio.set_event_loop(loop)
41
36
try:
42
# Is running event loop
43
37
asyncio.get_running_loop()
44
38
if not hasattr(loop.__class__, "_nest_patched"):
45
39
import nest_asyncio
46
40
nest_asyncio.apply(loop)
47
41
except RuntimeError:
48
# No running event loop
49
42
pass
50
43
except ImportError:
51
44
raise RuntimeError(
52
'Use "create_async" instead of "create" function in a running event loop. Or install the "nest_asyncio" package.'
45
'Use "create_async" instead of "create" function in a running event loop. Or install "nest_asyncio" package.'
53
46
)
54
47
return loop
55
48
56
def init_cookies():
57
urls = [
58
'https://chat-gpt.org',
59
'https://www.aitianhu.com',
60
'https://chatgptfree.ai',
61
'https://gptchatly.com',
62
'https://bard.google.com',
63
'https://huggingface.co/chat',
64
'https://open-assistant.io/chat'
65
]
66
67
browsers = ['google-chrome', 'chrome', 'firefox', 'safari']
68
69
def open_urls_in_browser(browser):
70
b = webbrowser.get(browser)
71
for url in urls:
72
b.open(url, new=0, autoraise=True)
73
74
for browser in browsers:
75
try:
76
open_urls_in_browser(browser)
77
break
78
except webbrowser.Error:
79
continue
80
81
# Check for broken dbus address in docker image
82
49
if os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
83
50
_LinuxPasswordManager.get_password = lambda a, b: b"secret"
84
85
# Load cookies for a domain from all supported browsers.
86
# Cache the results in the "_cookies" variable.
87
def get_cookies(domain_name=''):
51
52
def get_cookies(domain_name: str = '') -> Dict[str, str]:
53
"""
54
Load cookies for a given domain from all supported browsers and cache the results.
55
56
Args:
57
domain_name (str): The domain for which to load cookies.
58
59
Returns:
60
Dict[str, str]: A dictionary of cookie names and values.
61
"""
88
62
if domain_name in _cookies:
89
63
return _cookies[domain_name]
90
def g4f(domain_name):
91
user_data_dir = user_config_dir("g4f")
92
cookie_file = path.join(user_data_dir, "Default", "Cookies")
93
return [] if not path.exists(cookie_file) else chrome(cookie_file, domain_name)
64
65
cookies = _load_cookies_from_browsers(domain_name)
66
_cookies[domain_name] = cookies
67
return cookies
68
69
def _load_cookies_from_browsers(domain_name: str) -> Dict[str, str]:
70
"""
71
Helper function to load cookies from various browsers.
72
73
Args:
74
domain_name (str): The domain for which to load cookies.
94
75
76
Returns:
77
Dict[str, str]: A dictionary of cookie names and values.
78
"""
95
79
cookies = {}
96
for cookie_fn in [g4f, chrome, chromium, opera, opera_gx, brave, edge, vivaldi, firefox]:
80
for cookie_fn in [_g4f, chrome, chromium, opera, opera_gx, brave, edge, vivaldi, firefox]:
97
81
try:
98
82
cookie_jar = cookie_fn(domain_name=domain_name)
99
83
if len(cookie_jar) and debug.logging:
@@ -101,13 +85,38 @@ def get_cookies(domain_name=''):
101
85
for cookie in cookie_jar:
102
86
if cookie.name not in cookies:
103
87
cookies[cookie.name] = cookie.value
104
except:
88
except BrowserCookieError:
105
89
pass
106
_cookies[domain_name] = cookies
107
return _cookies[domain_name]
90
except Exception as e:
91
if debug.logging:
92
print(f"Error reading cookies from {cookie_fn.__name__} for {domain_name}: {e}")
93
return cookies
94
95
def _g4f(domain_name: str) -> list:
96
"""
97
Load cookies from the 'g4f' browser (if exists).
98
99
Args:
100
domain_name (str): The domain for which to load cookies.
108
101
102
Returns:
103
list: List of cookies.
104
"""
105
user_data_dir = user_config_dir("g4f")
106
cookie_file = os.path.join(user_data_dir, "Default", "Cookies")
107
return [] if not os.path.exists(cookie_file) else chrome(cookie_file, domain_name)
109
108
110
109
def format_prompt(messages: Messages, add_special_tokens=False) -> str:
110
"""
111
Format a series of messages into a single string, optionally adding special tokens.
112
113
Args:
114
messages (Messages): A list of message dictionaries, each containing 'role' and 'content'.
115
add_special_tokens (bool): Whether to add special formatting tokens.
116
117
Returns:
118
str: A formatted string containing all messages.
119
"""
111
120
if not add_special_tokens and len(messages) <= 1:
112
121
return messages[0]["content"]
113
122
formatted = "\n".join([
@@ -116,12 +125,26 @@ def format_prompt(messages: Messages, add_special_tokens=False) -> str:
116
125
])
117
126
return f"{formatted}\nAssistant:"
118
127
119
120
128
def get_random_string(length: int = 10) -> str:
129
"""
130
Generate a random string of specified length, containing lowercase letters and digits.
131
132
Args:
133
length (int, optional): Length of the random string to generate. Defaults to 10.
134
135
Returns:
136
str: A random string of the specified length.
137
"""
121
138
return ''.join(
122
139
random.choice(string.ascii_lowercase + string.digits)
123
140
for _ in range(length)
124
141
)
125
142
126
143
def get_random_hex() -> str:
144
"""
145
Generate a random hexadecimal string of a fixed length.
146
147
Returns:
148
str: A random hexadecimal string of 32 characters (16 bytes).
149
"""
127
150
return secrets.token_hex(16).zfill(32)
@@ -404,7 +404,7 @@ body {
404
404
display: none;
405
405
}
406
406
407
#image {
407
#image, #file {
408
408
display: none;
409
409
}
410
410
@@ -412,13 +412,22 @@ label[for="image"]:has(> input:valid){
412
412
color: var(--accent);
413
413
}
414
414
415
label[for="image"] {
415
label[for="file"]:has(> input:valid){
416
color: var(--accent);
417
}
418
419
label[for="image"], label[for="file"] {
416
420
cursor: pointer;
417
421
position: absolute;
418
422
top: 10px;
419
423
left: 10px;
420
424
}
421
425
426
label[for="file"] {
427
top: 32px;
428
left: 10px;
429
}
430
422
431
.buttons input[type="checkbox"] {
423
432
height: 0;
424
433
width: 0;
@@ -31,12 +31,21 @@ from .Provider import (
31
31
32
32
@dataclass(unsafe_hash=True)
33
33
class Model:
34
"""
35
Represents a machine learning model configuration.
36
37
Attributes:
38
name (str): Name of the model.
39
base_provider (str): Default provider for the model.
40
best_provider (ProviderType): The preferred provider for the model, typically with retry logic.
41
"""
34
42
name: str
35
43
base_provider: str
36
44
best_provider: ProviderType = None
37
45
38
46
@staticmethod
39
47
def __all__() -> list[str]:
48
"""Returns a list of all model names."""
40
49
return _all_models
41
50
42
51
default = Model(
@@ -298,6 +307,12 @@ pi = Model(
298
307
)
299
308
300
309
class ModelUtils:
310
"""
311
Utility class for mapping string identifiers to Model instances.
312
313
Attributes:
314
convert (dict[str, Model]): Dictionary mapping model string identifiers to Model instances.
315
"""
301
316
convert: dict[str, Model] = {
302
317
# gpt-3.5
303
318
'gpt-3.5-turbo' : gpt_35_turbo,