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

XFEstudio/gpt4free

Support continue messages in Airforce Add auth caching for OpenAI ChatGPT Some provider improvments

6e0bc147
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

17 个文件 +284 -341
Modified g4f/Provider/Airforce.py +12 -2
@@ -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,
Modified g4f/Provider/Copilot.py +3 -1
@@ -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"]:
Modified g4f/Provider/needs_auth/Gemini.py +2 -2
@@ -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
Modified g4f/Provider/needs_auth/HuggingFace.py +1 -1
@@ -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
Modified g4f/Provider/needs_auth/OpenaiChat.py +57 -43
@@ -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
Modified g4f/__init__.py +8 -10
@@ -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
Modified g4f/client/__init__.py +7 -18
@@ -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)
Modified g4f/client/helper.py +1 -6
@@ -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}")
Modified g4f/gui/client/home.html +16 -3
@@ -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 = `
Modified g4f/gui/client/static/css/style.css +3 -3
@@ -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 {
Modified g4f/gui/client/static/js/chat.v1.js +3 -1
@@ -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");
Modified g4f/providers/asyncio.py +23 -4
@@ -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
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