返回提交历史
Modified
g4f/Provider/Airforce.py
+12
-2
Modified
g4f/Provider/Copilot.py
+3
-1
Modified
g4f/Provider/needs_auth/Gemini.py
+2
-2
Modified
g4f/Provider/needs_auth/HuggingFace.py
+1
-1
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+57
-43
Modified
g4f/__init__.py
+8
-10
Modified
g4f/client/__init__.py
+7
-18
Modified
g4f/client/helper.py
+1
-6
Modified
g4f/gui/client/home.html
+16
-3
Modified
g4f/gui/client/static/css/style.css
+3
-3
Modified
g4f/gui/client/static/js/chat.v1.js
+3
-1
Modified
g4f/providers/asyncio.py
+23
-4
Modified
g4f/providers/base_provider.py
+100
-103
Modified
g4f/providers/retry_provider.py
+37
-107
Modified
g4f/providers/types.py
+7
-31
Modified
g4f/tools/run_tools.py
+2
-4
Modified
g4f/tools/web_search.py
+2
-2
XFEstudio/gpt4free
Support continue messages in Airforce Add auth caching for OpenAI ChatGPT Some provider improvments
6e0bc147
代码差异
17 个文件
+284
-341
@@ -7,6 +7,7 @@ from typing import List
7
7
8
8
from ..typing import AsyncResult, Messages
9
9
from ..image import ImageResponse
10
from ..providers.response import FinishReason, Usage
10
11
from ..requests.raise_for_status import raise_for_status
11
12
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
12
13
@@ -232,17 +233,19 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
232
233
data = {
233
234
"messages": final_messages,
234
235
"model": model,
235
"max_tokens": max_tokens,
236
236
"temperature": temperature,
237
237
"top_p": top_p,
238
238
"stream": stream,
239
239
}
240
if max_tokens != 512:
241
data["max_tokens"] = max_tokens
240
242
241
243
async with ClientSession(headers=headers) as session:
242
244
async with session.post(cls.api_endpoint_completions, json=data, proxy=proxy) as response:
243
245
await raise_for_status(response)
244
246
245
247
if stream:
248
idx = 0
246
249
async for line in response.content:
247
250
line = line.decode('utf-8').strip()
248
251
if line.startswith('data: '):
@@ -255,11 +258,18 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
255
258
chunk = cls._filter_response(delta['content'])
256
259
if chunk:
257
260
yield chunk
261
idx += 1
258
262
except json.JSONDecodeError:
259
263
continue
264
if idx == 512:
265
yield FinishReason("length")
260
266
else:
261
267
# Non-streaming response
262
268
result = await response.json()
269
if "usage" in result:
270
yield Usage(**result["usage"])
271
if result["usage"]["completion_tokens"] == 512:
272
yield FinishReason("length")
263
273
if 'choices' in result and result['choices']:
264
274
message = result['choices'][0].get('message', {})
265
275
content = message.get('content', '')
@@ -273,7 +283,7 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
273
283
messages: Messages,
274
284
prompt: str = None,
275
285
proxy: str = None,
276
max_tokens: int = 4096,
286
max_tokens: int = 512,
277
287
temperature: float = 1,
278
288
top_p: float = 1,
279
289
stream: bool = True,
@@ -90,7 +90,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
90
90
cls._access_token, cls._cookies = asyncio.run(get_access_token_and_cookies(cls.url, proxy))
91
91
else:
92
92
raise h
93
yield Parameters(**{"api_key": cls._access_token, "cookies": cls._cookies})
93
yield Parameters(**{"api_key": cls._access_token, "cookies": cls._cookies if isinstance(cls._cookies, dict) else {c.name: c.value for c in cls._cookies}})
94
94
websocket_url = f"{websocket_url}&accessToken={quote(cls._access_token)}"
95
95
headers = {"authorization": f"Bearer {cls._access_token}"}
96
96
@@ -191,6 +191,8 @@ class Copilot(AbstractProvider, ProviderModelMixin):
191
191
yield ImageResponse(msg.get("url"), image_prompt, {"preview": msg.get("thumbnailUrl")})
192
192
elif msg.get("event") == "done":
193
193
break
194
elif msg.get("event") == "replaceText":
195
yield msg.get("text")
194
196
elif msg.get("event") == "error":
195
197
raise RuntimeError(f"Error: {msg}")
196
198
elif msg.get("event") not in ["received", "startMessage", "citation", "partCompleted"]:
@@ -323,13 +323,13 @@ async def iter_filter_base64(chunks: AsyncIterator[bytes]) -> AsyncIterator[byte
323
323
async for chunk in chunks:
324
324
if is_started:
325
325
if end_with in chunk:
326
yield chunk.split(end_with, 1, maxsplit=1).pop(0)
326
yield chunk.split(end_with, maxsplit=1).pop(0)
327
327
break
328
328
else:
329
329
yield chunk
330
330
elif search_for in chunk:
331
331
is_started = True
332
yield chunk.split(search_for, 1, maxsplit=1).pop()
332
yield chunk.split(search_for, maxsplit=1).pop()
333
333
else:
334
334
raise ValueError(f"Response: {chunk}")
335
335
@@ -111,7 +111,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
111
111
messages = [m for m in messages if m["role"] == "system"] + [messages[-1]]
112
112
inputs = get_inputs(messages, model_data, model_type, do_continue)
113
113
debug.log(f"New len: {len(inputs)}")
114
if model_type == "gpt2" and max_new_tokens >= 1024:
114
if model_type == "gpt2" and max_tokens >= 1024:
115
115
params["max_new_tokens"] = 512
116
116
payload = {"inputs": inputs, "parameters": params, "stream": stream}
117
117
@@ -17,14 +17,14 @@ try:
17
17
except ImportError:
18
18
has_nodriver = False
19
19
20
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
20
from ..base_provider import AsyncAuthedProvider, ProviderModelMixin
21
21
from ...typing import AsyncResult, Messages, Cookies, ImagesType
22
22
from ...requests.raise_for_status import raise_for_status
23
23
from ...requests import StreamSession
24
24
from ...requests import get_nodriver
25
25
from ...image import ImageResponse, ImageRequest, to_image, to_bytes, is_accepted_format
26
26
from ...errors import MissingAuthError, NoValidHarFileError
27
from ...providers.response import JsonConversation, FinishReason, SynthesizeData
27
from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult
28
28
from ...providers.response import Sources, TitleGeneration, RequestLogin, Parameters
29
29
from ..helper import format_cookies
30
30
from ..openai.har_file import get_request_config
@@ -85,7 +85,7 @@ UPLOAD_HEADERS = {
85
85
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
86
86
}
87
87
88
class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
88
class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
89
89
"""A class for creating and managing conversations with OpenAI chat service"""
90
90
91
91
label = "OpenAI ChatGPT"
@@ -104,6 +104,20 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
104
104
_cookies: Cookies = None
105
105
_expires: int = None
106
106
107
@classmethod
108
async def on_auth_async(cls, **kwargs) -> AuthResult:
109
if cls.needs_auth:
110
async for _ in cls.login():
111
pass
112
return AuthResult(
113
api_key=cls._api_key,
114
cookies=cls._cookies or RequestConfig.cookies or {},
115
headers=cls._headers or RequestConfig.headers or cls.get_default_headers(),
116
expires=cls._expires,
117
proof_token=RequestConfig.proof_token,
118
turnstile_token=RequestConfig.turnstile_token
119
)
120
107
121
@classmethod
108
122
def get_models(cls, proxy: str = None, timeout: int = 180) -> List[str]:
109
123
if not cls.models:
@@ -135,7 +149,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
135
149
async def upload_images(
136
150
cls,
137
151
session: StreamSession,
138
headers: dict,
152
auth_result: AuthResult,
139
153
images: ImagesType,
140
154
) -> ImageRequest:
141
155
"""
@@ -160,8 +174,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
160
174
"use_case": "multimodal"
161
175
}
162
176
# Post the image data to the service and get the image data
163
async with session.post(f"{cls.url}/backend-api/files", json=data, headers=headers) as response:
164
cls._update_request_args(session)
177
async with session.post(f"{cls.url}/backend-api/files", json=data, headers=auth_result.headers) as response:
178
cls._update_request_args(auth_result, session)
165
179
await raise_for_status(response, "Create file failed")
166
180
image_data = {
167
181
**data,
@@ -189,9 +203,9 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
189
203
async with session.post(
190
204
f"{cls.url}/backend-api/files/{image_data['file_id']}/uploaded",
191
205
json={},
192
headers=headers
206
headers=auth_result.headers
193
207
) as response:
194
cls._update_request_args(session)
208
cls._update_request_args(auth_result, session)
195
209
await raise_for_status(response, "Get download url failed")
196
210
image_data["download_url"] = (await response.json())["download_url"]
197
211
return ImageRequest(image_data)
@@ -248,7 +262,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
248
262
return messages
249
263
250
264
@classmethod
251
async def get_generated_image(cls, session: StreamSession, headers: dict, element: dict, prompt: str = None) -> ImageResponse:
265
async def get_generated_image(cls, auth_result: AuthResult, session: StreamSession, element: dict, prompt: str = None) -> ImageResponse:
252
266
try:
253
267
prompt = element["metadata"]["dalle"]["prompt"]
254
268
file_id = element["asset_pointer"].split("file-service://", 1)[1]
@@ -257,8 +271,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
257
271
except Exception as e:
258
272
raise RuntimeError(f"No Image: {e.__class__.__name__}: {e}")
259
273
try:
260
async with session.get(f"{cls.url}/backend-api/files/{file_id}/download", headers=headers) as response:
261
cls._update_request_args(session)
274
async with session.get(f"{cls.url}/backend-api/files/{file_id}/download", headers=auth_result.headers) as response:
275
cls._update_request_args(auth_result, session)
262
276
await raise_for_status(response)
263
277
download_url = (await response.json())["download_url"]
264
278
return ImageResponse(download_url, prompt)
@@ -266,10 +280,11 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
266
280
raise RuntimeError(f"Error in downloading image: {e}")
267
281
268
282
@classmethod
269
async def create_async_generator(
283
async def create_authed(
270
284
cls,
271
285
model: str,
272
286
messages: Messages,
287
auth_result: AuthResult,
273
288
proxy: str = None,
274
289
timeout: int = 180,
275
290
auto_continue: bool = False,
@@ -279,7 +294,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
279
294
conversation: Conversation = None,
280
295
images: ImagesType = None,
281
296
return_conversation: bool = False,
282
max_retries: int = 3,
297
max_retries: int = 0,
283
298
web_search: bool = False,
284
299
**kwargs
285
300
) -> AsyncResult:
@@ -306,9 +321,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
306
321
Raises:
307
322
RuntimeError: If an error occurs during processing.
308
323
"""
309
if cls.needs_auth:
310
async for message in cls.login(proxy, **kwargs):
311
yield message
312
324
async with StreamSession(
313
325
proxy=proxy,
314
326
impersonate="chrome",
@@ -319,15 +331,18 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
319
331
if cls._headers is None:
320
332
cls._create_request_args(cls._cookies)
321
333
async with session.get(cls.url, headers=INIT_HEADERS) as response:
322
cls._update_request_args(session)
334
cls._update_request_args(auth_result, session)
323
335
await raise_for_status(response)
324
336
else:
325
print(cls._headers)
326
async with session.get(cls.url, headers=cls._headers) as response:
327
cls._update_request_args(session)
337
if cls._headers is None:
338
cls._create_request_args(auth_result.cookies, auth_result.headers)
339
if not cls._set_api_key(auth_result.api_key):
340
raise MissingAuthError("Access token is not valid")
341
async with session.get(cls.url, headers=auth_result.headers) as response:
342
cls._update_request_args(auth_result, session)
328
343
await raise_for_status(response)
329
344
try:
330
image_requests = await cls.upload_images(session, cls._headers, images) if images else None
345
image_requests = await cls.upload_images(session, auth_result, images) if images else None
331
346
except Exception as e:
332
347
debug.log("OpenaiChat: Upload image failed")
333
348
debug.log(f"{e.__class__.__name__}: {e}")
@@ -345,36 +360,36 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
345
360
f"{cls.url}/backend-anon/sentinel/chat-requirements"
346
361
if cls._api_key is None else
347
362
f"{cls.url}/backend-api/sentinel/chat-requirements",
348
json={"p": get_requirements_token(RequestConfig.proof_token) if RequestConfig.proof_token else None},
363
json={"p": None if auth_result.proof_token is None else get_requirements_token(auth_result.proof_token)},
349
364
headers=cls._headers
350
365
) as response:
351
366
if response.status == 401:
352
367
cls._headers = cls._api_key = None
353
368
else:
354
cls._update_request_args(session)
369
cls._update_request_args(auth_result, session)
355
370
await raise_for_status(response)
356
371
chat_requirements = await response.json()
357
372
need_turnstile = chat_requirements.get("turnstile", {}).get("required", False)
358
373
need_arkose = chat_requirements.get("arkose", {}).get("required", False)
359
374
chat_token = chat_requirements.get("token")
360
375
361
if need_arkose and RequestConfig.arkose_token is None:
362
await get_request_config(proxy)
363
cls._create_request_args(RequestConfig.cookies, RequestConfig.headers)
364
cls._set_api_key(RequestConfig.access_token)
365
if RequestConfig.arkose_token is None:
366
raise MissingAuthError("No arkose token found in .har file")
376
# if need_arkose and RequestConfig.arkose_token is None:
377
# await get_request_config(proxy)
378
# cls._create_request_args(auth_result.cookies, auth_result.headers)
379
# cls._set_api_key(auth_result.access_token)
380
# if auth_result.arkose_token is None:
381
# raise MissingAuthError("No arkose token found in .har file")
367
382
368
383
if "proofofwork" in chat_requirements:
369
if RequestConfig.proof_token is None:
370
RequestConfig.proof_token = get_config(cls._headers.get("user-agent"))
384
if auth_result.proof_token is None:
385
auth_result.proof_token = get_config(auth_result.headers.get("user-agent"))
371
386
proofofwork = generate_proof_token(
372
387
**chat_requirements["proofofwork"],
373
user_agent=cls._headers.get("user-agent"),
374
proof_token=RequestConfig.proof_token
388
user_agent=auth_result.headers.get("user-agent"),
389
proof_token=auth_result.proof_token
375
390
)
376
391
[debug.log(text) for text in (
377
f"Arkose: {'False' if not need_arkose else RequestConfig.arkose_token[:12]+'...'}",
392
#f"Arkose: {'False' if not need_arkose else auth_result.arkose_token[:12]+'...'}",
378
393
f"Proofofwork: {'False' if proofofwork is None else proofofwork[:12]+'...'}",
379
394
f"AccessToken: {'False' if cls._api_key is None else cls._api_key[:12]+'...'}",
380
395
)]
@@ -414,12 +429,12 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
414
429
"content-type": "application/json",
415
430
"openai-sentinel-chat-requirements-token": chat_token,
416
431
}
417
if RequestConfig.arkose_token:
418
headers["openai-sentinel-arkose-token"] = RequestConfig.arkose_token
432
#if RequestConfig.arkose_token:
433
# headers["openai-sentinel-arkose-token"] = RequestConfig.arkose_token
419
434
if proofofwork is not None:
420
435
headers["openai-sentinel-proof-token"] = proofofwork
421
if need_turnstile and RequestConfig.turnstile_token is not None:
422
headers['openai-sentinel-turnstile-token'] = RequestConfig.turnstile_token
436
if need_turnstile and auth_result.turnstile_token is not None:
437
headers['openai-sentinel-turnstile-token'] = auth_result.turnstile_token
423
438
async with session.post(
424
439
f"{cls.url}/backend-anon/conversation"
425
440
if cls._api_key is None else
@@ -427,7 +442,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
427
442
json=data,
428
443
headers=headers
429
444
) as response:
430
cls._update_request_args(session)
445
cls._update_request_args(auth_result, session)
431
446
if response.status in (403, 404) and max_retries > 0:
432
447
max_retries -= 1
433
448
debug.log(f"Retry: Error {response.status}: {await response.text()}")
@@ -462,7 +477,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
462
477
yield sources
463
478
if return_conversation:
464
479
yield conversation
465
if not history_disabled and cls._api_key is not None:
480
if not history_disabled and auth_result.api_key is not None:
466
481
yield SynthesizeData(cls.__name__, {
467
482
"conversation_id": conversation.conversation_id,
468
483
"message_id": conversation.message_id,
@@ -587,7 +602,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
587
602
try:
588
603
await get_request_config(proxy)
589
604
cls._create_request_args(RequestConfig.cookies, RequestConfig.headers)
590
print(RequestConfig.access_token)
591
605
if RequestConfig.access_token is not None or cls.needs_auth:
592
606
if not cls._set_api_key(RequestConfig.access_token):
593
607
raise NoValidHarFileError(f"Access token is not valid: {RequestConfig.access_token}")
@@ -673,9 +687,9 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
673
687
cls._update_cookie_header()
674
688
675
689
@classmethod
676
def _update_request_args(cls, session: StreamSession):
690
def _update_request_args(cls, auth_result: AuthResult, session: StreamSession):
677
691
for c in session.cookie_jar if hasattr(session, "cookie_jar") else session.cookies.jar:
678
cls._cookies[getattr(c, "key", getattr(c, "name", ""))] = c.value
692
auth_result.cookies[getattr(c, "key", getattr(c, "name", ""))] = c.value
679
693
cls._update_cookie_header()
680
694
681
695
@classmethod
@@ -11,7 +11,7 @@ from .typing import Messages, CreateResult, AsyncResult, ImageType
11
11
from .errors import StreamNotSupportedError
12
12
from .cookies import get_cookies, set_cookies
13
13
from .providers.types import ProviderType
14
from .providers.helper import concat_chunks
14
from .providers.helper import concat_chunks, async_concat_chunks
15
15
from .client.service import get_model_and_provider
16
16
17
17
#Configure "g4f" logger
@@ -47,8 +47,7 @@ class ChatCompletion:
47
47
if ignore_stream:
48
48
kwargs["ignore_stream"] = True
49
49
50
create_method = provider.create_authed if hasattr(provider, "create_authed") else provider.create_completion
51
result = create_method(model, messages, stream=stream, **kwargs)
50
result = provider.get_create_function()(model, messages, stream=stream, **kwargs)
52
51
53
52
return result if stream else concat_chunks(result)
54
53
@@ -72,11 +71,10 @@ class ChatCompletion:
72
71
if ignore_stream:
73
72
kwargs["ignore_stream"] = True
74
73
75
if stream:
76
if hasattr(provider, "create_async_authed_generator"):
77
return provider.create_async_authed_generator(model, messages, **kwargs)
78
elif hasattr(provider, "create_async_generator"):
79
return provider.create_async_generator(model, messages, **kwargs)
80
raise StreamNotSupportedError(f'{provider.__name__} does not support "stream" argument in "create_async"')
74
result = provider.get_async_create_function()(model, messages, stream=stream, **kwargs)
81
75
82
return provider.create_async(model, messages, **kwargs)
76
if not stream:
77
if hasattr(result, "__aiter__"):
78
result = async_concat_chunks(result)
79
80
return result
@@ -15,14 +15,14 @@ from ..providers.types import ProviderType, BaseRetryProvider
15
15
from ..providers.response import ResponseType, FinishReason, BaseConversation, SynthesizeData, ToolCalls, Usage
16
16
from ..errors import NoImageResponseError
17
17
from ..providers.retry_provider import IterListProvider
18
from ..providers.asyncio import to_sync_generator, async_generator_to_list
18
from ..providers.asyncio import to_sync_generator
19
19
from ..Provider.needs_auth import BingCreateImages, OpenaiAccount
20
20
from ..tools.run_tools import async_iter_run_tools, iter_run_tools
21
21
from .stubs import ChatCompletion, ChatCompletionChunk, Image, ImagesResponse
22
22
from .image_models import ImageModels
23
23
from .types import IterResponse, ImageProvider, Client as BaseClient
24
24
from .service import get_model_and_provider, convert_to_provider
25
from .helper import find_stop, filter_json, filter_none, safe_aclose, to_async_iterator
25
from .helper import find_stop, filter_json, filter_none, safe_aclose
26
26
from .. import debug
27
27
28
28
ChatCompletionResponseType = Iterator[Union[ChatCompletion, ChatCompletionChunk, BaseConversation]]
@@ -236,7 +236,7 @@ class Completions:
236
236
kwargs["ignore_stream"] = True
237
237
238
238
response = iter_run_tools(
239
provider.create_authed if hasattr(provider, "create_authed") else provider.create_completion,
239
provider.get_create_function(),
240
240
model,
241
241
messages,
242
242
stream=stream,
@@ -248,12 +248,9 @@ class Completions:
248
248
),
249
249
**kwargs
250
250
)
251
if stream and hasattr(response, '__aiter__'):
252
# It's an async generator, wrap it into a sync iterator
253
response = to_sync_generator(response)
254
elif hasattr(response, '__aiter__'):
255
# If response is an async generator, collect it into a list
256
response = asyncio.run(async_generator_to_list(response))
251
if not hasattr(response, '__iter__'):
252
response = [response]
253
257
254
response = iter_response(response, stream, response_format, max_tokens, stop)
258
255
response = iter_append_model_and_provider(response, model, provider)
259
256
if stream:
@@ -526,14 +523,8 @@ class AsyncCompletions:
526
523
kwargs["images"] = [(image, image_name)]
527
524
if ignore_stream:
528
525
kwargs["ignore_stream"] = True
529
if hasattr(provider, "create_async_authed_generator"):
530
create_handler = provider.create_async_authed_generator
531
if hasattr(provider, "create_async_generator"):
532
create_handler = provider.create_async_generator
533
else:
534
create_handler = provider.create_completion
535
526
response = async_iter_run_tools(
536
create_handler,
527
provider.get_async_create_function(),
537
528
model,
538
529
messages,
539
530
stream=stream,
@@ -545,8 +536,6 @@ class AsyncCompletions:
545
536
),
546
537
**kwargs
547
538
)
548
if not hasattr(response, '__aiter__'):
549
response = to_async_iterator(response)
550
539
response = async_iter_response(response, stream, response_format, max_tokens, stop)
551
540
response = async_iter_append_model_and_provider(response, model, provider)
552
541
return response if stream else anext(response)
@@ -62,9 +62,4 @@ async def safe_aclose(generator: AsyncGenerator) -> None:
62
62
if generator and hasattr(generator, 'aclose'):
63
63
await generator.aclose()
64
64
except Exception as e:
65
logging.warning(f"Error while closing generator: {e}")
66
67
# Helper function to convert a synchronous iterator to an async iterator
68
async def to_async_iterator(iterator: Iterator) -> AsyncIterator:
69
for item in iterator:
70
yield item
65
logging.warning(f"Error while closing generator: {e}")
@@ -175,6 +175,7 @@
175
175
}
176
176
}
177
177
</style>
178
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
178
179
</head>
179
180
<body>
180
181
<iframe id="background"></iframe>
@@ -206,12 +207,24 @@
206
207
<p>Powered by the G4F framework</p>
207
208
</div>
208
209
209
<iframe id="stream-widget" class="stream" data-src="/backend-api/v2/create?prompt=Create of overview of the news in plain text&stream=1&web_search=news in " class="" frameborder="0"></iframe>
210
<iframe id="stream-widget" class="stream" frameborder="0"></iframe>
210
211
</div>
211
212
<script>
212
const iframe = document.getElementById('stream-widget');
213
iframe.src = iframe.dataset.src + navigator.language;
213
const iframe = document.getElementById('stream-widget');""
214
let search = (navigator.language == "de" ? "news in deutschland" : navigator.language == "en" ? "world news" : navigator.language);
215
if (Math.floor(Math.random() * 6) % 2 == 0) {
216
search = "xtekky/gpt4free releases";
217
}
218
const url = "/backend-api/v2/create?prompt=Create of overview of the news in plain text&stream=1&web_search=" + search;
219
iframe.src = url;
214
220
setTimeout(()=>iframe.classList.add('show'), 3000);
221
iframe.onload = () => {
222
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
223
const iframeBody = iframeDocument.querySelector("body");
224
const iframeContent = iframeDocument.querySelector("pre");
225
const markdown = window.markdownit();
226
iframeBody.innerHTML = markdown.render(iframeContent.innerHTML);
227
}
215
228
216
229
(async () => {
217
230
const prompt = `
@@ -564,7 +564,7 @@ body:not(.white) a:visited{
564
564
height: 20px;
565
565
width: 100px;
566
566
transition: all 0.1s;
567
background: var(--colour-5);
567
background: var(--button-hover);
568
568
margin-top: -30px;
569
569
z-index: 1005;
570
570
padding: 6px;
@@ -683,7 +683,7 @@ label[for="camera"] {
683
683
#messages form {
684
684
position: absolute;
685
685
width: 100%;
686
background: var(--colour-5);
686
background: var(--button-hover);
687
687
z-index: 2000;
688
688
}
689
689
@@ -1354,7 +1354,7 @@ form .field.saved .fa-xmark {
1354
1354
.settings .label, form .label, .settings label, form label {
1355
1355
font-size: 15px;
1356
1356
margin-left: var(--inner-gap);
1357
min-width: 120px;
1357
min-width: 200px;
1358
1358
}
1359
1359
1360
1360
.settings .label, form .label {
@@ -2111,8 +2111,10 @@ if (SpeechRecognition) {
2111
2111
microLabel.classList.add("recognition");
2112
2112
startValue = messageInput.value;
2113
2113
lastDebounceTranscript = "";
2114
messageInput.readOnly = true;
2114
2115
};
2115
2116
recognition.onend = function() {
2117
messageInput.readOnly = false;
2116
2118
messageInput.focus();
2117
2119
};
2118
2120
recognition.onresult = function(event) {
@@ -2138,7 +2140,7 @@ if (SpeechRecognition) {
2138
2140
}
2139
2141
};
2140
2142
2141
microLabel.addEventListener("click", () => {
2143
microLabel.addEventListener("click", (e) => {
2142
2144
if (microLabel.classList.contains("recognition")) {
2143
2145
recognition.stop();
2144
2146
microLabel.classList.remove("recognition");
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
3
3
import asyncio
4
4
from asyncio import AbstractEventLoop, runners
5
from typing import Optional, Callable, AsyncGenerator, Generator
5
from typing import Optional, Callable, AsyncIterator, Iterator
6
6
7
7
from ..errors import NestAsyncioError
8
8
@@ -37,10 +37,14 @@ def get_running_loop(check_nested: bool) -> Optional[AbstractEventLoop]:
37
37
async def await_callback(callback: Callable):
38
38
return await callback()
39
39
40
async def async_generator_to_list(generator: AsyncGenerator) -> list:
40
async def async_generator_to_list(generator: AsyncIterator) -> list:
41
41
return [item async for item in generator]
42
42
43
def to_sync_generator(generator: AsyncGenerator) -> Generator:
43
def to_sync_generator(generator: AsyncIterator, stream: bool = True) -> Iterator:
44
if not stream:
45
yield from asyncio.run(async_generator_to_list(generator))
46
return
47
44
48
loop = get_running_loop(check_nested=False)
45
49
new_loop = False
46
50
if loop is None:
@@ -62,4 +66,19 @@ def to_sync_generator(generator: AsyncGenerator) -> Generator:
62
66
loop.run_until_complete(loop.shutdown_default_executor())
63
67
finally:
64
68
asyncio.set_event_loop(None)
65
loop.close()
69
loop.close()
70
71
# Helper function to convert a synchronous iterator to an async iterator
72
async def to_async_iterator(iterator: Iterator) -> AsyncIterator:
73
if isinstance(iterator, str):
74
yield iterator
75
elif hasattr(iterator, "__await__"):
76
yield await iterator
77
elif hasattr(iterator, "__aiter__"):
78
async for item in iterator:
79
yield item
80
elif hasattr(iterator, "__iter__"):
81
for item in iterator:
82
yield item
83
else:
84
yield iterator