From da6c00e2a2f3c176d832b4beb7baed9fd83aa92c Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:34:45 +0100 Subject: [PATCH] Refactor Cloudflare and LMArena providers to enhance authentication handling and improve WebSocket communication --- .gitignore | 57 ++++++------------------------ g4f/Provider/needs_auth/LMArena.py | 17 ++++++--- g4f/Provider/needs_auth/Video.py | 20 +++++++---- g4f/Provider/qwen/QwenCode.py | 18 ++++++---- g4f/api/__init__.py | 15 ++++---- g4f/image/__init__.py | 5 +++ 6 files changed, 58 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 358d9178..4ea6d3b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,11 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml - -# Ignore local python virtual environment -venv/ - -# Ignore streamlit_chat_app.py conversations pickle -conversations.pkl -*.pkl -.idea/ -**/__pycache__/ -__pycache__/ - -*.log -*.pyc -*.egg-info/ -*.egg -*.egg-info -.DS_Store -*~ -*.gguf -.buildozer -har_and_cookies +bin +dist +__pycache__ +generated_media +site-packages +projects node_modules -models -projects/windows/g4f -generated_images/ -generated_media/ -projects/windows/ - -*.bak -*.backup -.env -g4f.dev/ - -# Build artifacts -build/ -dist/ -*.spec -pyproject.toml.bak -debian/ -winget/ \ No newline at end of file +g4f.egg-info +models/models.json +pyvenv.cfg +lib64 diff --git a/g4f/Provider/needs_auth/LMArena.py b/g4f/Provider/needs_auth/LMArena.py index 7753cfa2..ade89aab 100644 --- a/g4f/Provider/needs_auth/LMArena.py +++ b/g4f/Provider/needs_auth/LMArena.py @@ -568,15 +568,22 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin): pass elif has_nodriver or cls.share_url is None: async def callback(page): - element = await page.select('[style="display: grid;"]') - if element: - await click_trunstile(page, 'document.querySelector(\'[style="display: grid;"]\')') - await page.find("Ask anything…", 120) button = await page.find("Accept Cookies") if button: await button.click() else: debug.log("No 'Accept Cookies' button found, skipping.") + await asyncio.sleep(1) + textarea = await page.find("Ask anything…") + if textarea: + await textarea.send_keys("Hello") + await asyncio.sleep(1) + button = await page.select('[type="submit"]:has([data-sentry-element="ArrowUp"])') + if button: + await button.click() + element = await page.select('[style="display: grid;"]') + if element: + await click_trunstile(page, 'document.querySelector(\'[style="display: grid;"]\')') if not await page.evaluate('document.cookie.indexOf("arena-auth-prod-v1") >= 0'): debug.log("No authentication cookie found, trying to authenticate.") await page.select('#cf-turnstile', 300) @@ -628,7 +635,7 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin): elif model in cls.image_models: model_id = cls.image_models[model] else: - raise ModelNotFoundError(f"Model '{model}' is not supported by LMArena Beta.") + raise ModelNotFoundError(f"Model '{model}' is not supported by LMArena provider.") userMessageId = str(uuid.uuid7()) modelAMessageId = str(uuid.uuid7()) diff --git a/g4f/Provider/needs_auth/Video.py b/g4f/Provider/needs_auth/Video.py index c2b3d0a5..c394b900 100644 --- a/g4f/Provider/needs_auth/Video.py +++ b/g4f/Provider/needs_auth/Video.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import asyncio from typing import Optional from aiohttp import ClientSession, ClientTimeout @@ -10,13 +11,13 @@ from aiohttp import ClientSession try: import nodriver from nodriver.core.connection import ProtocolException + has_nodriver = True except: - pass + has_nodriver = False from ...typing import Messages, AsyncResult from ...providers.response import VideoResponse, Reasoning, ContinueResponse, ProviderInfo from ...requests import get_nodriver -from ...errors import MissingRequirementsError from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin from ..helper import format_media_prompt from ... import debug @@ -53,7 +54,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): "sora": "https://sora.chatgpt.com/explore", #"veo": "https://aistudio.google.com/generate-video" } - api_url = f"{PUBLIC_URL}/backend-api/v2/create?provider=Video&cache=true&prompt=" + api_path = f"?provider=Video&cache=true&prompt=" drive_url = "https://www.googleapis.com/drive/v3/" active_by_default = True @@ -62,10 +63,11 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): video_models = models needs_auth = True - working = True + working = has_nodriver browser = None stop_browser = None + share_url: Optional[str] = None @classmethod async def create_async_generator( @@ -77,6 +79,8 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): aspect_ratio: str = None, **kwargs ) -> AsyncResult: + if cls.share_url is None: + cls.share_url = os.getenv("G4F_SHARE_URL") if not model: model = cls.default_model if model not in cls.video_models: @@ -94,10 +98,12 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): yield Reasoning(label="Open browser") browser, stop_browser = await get_nodriver(proxy=proxy) except Exception as e: + if cls.share_url is None: + raise debug.error(f"Error getting nodriver:", e) async with ClientSession() as session: yield Reasoning(label="Generating") - async with session.get(cls.api_url + quote(prompt)) as response: + async with session.get(f"{cls.share_url}{cls.api_path + quote(prompt)}") as response: if not response.ok: debug.error(f"Failed to generate Video: {response.status}") else: @@ -108,7 +114,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): return yield VideoResponse(str(response.url), prompt) return - raise MissingRequirementsError("Video provider requires a browser to be installed.") + raise page = None try: yield ContinueResponse("Timeout waiting for Video URL") @@ -123,7 +129,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin): RequestConfig.headers = {} for key, value in event.request.headers.items(): RequestConfig.headers[key.lower()] = value - for _, urls in RequestConfig.urls.items(): + for urls in RequestConfig.urls.values(): if event.request.url in urls: return debug.log(f"Adding URL: {event.request.url}") diff --git a/g4f/Provider/qwen/QwenCode.py b/g4f/Provider/qwen/QwenCode.py index 8304aa8b..16207392 100644 --- a/g4f/Provider/qwen/QwenCode.py +++ b/g4f/Provider/qwen/QwenCode.py @@ -14,9 +14,7 @@ class QwenCode(OpenaiTemplate): needs_auth = True active_by_default = True default_model = "qwen3-coder-plus" - default_vision_model = "qwen-vl-max-latest" - models = [default_model, default_vision_model] - vision_models = [default_vision_model] + models = [default_model] client = QwenContentGenerator(QwenOAuth2Client()) @classmethod @@ -48,9 +46,12 @@ class QwenCode(OpenaiTemplate): api_base=creds.get("endpoint", api_base), **kwargs ): - if chunk != last_chunk: + if isinstance(chunk, str): + if chunk != last_chunk: + yield chunk + last_chunk = chunk + else: yield chunk - last_chunk = chunk except TokenManagerError: await cls.client.shared_manager.getValidCredentials(cls.client.qwen_client, True) creds = await cls.client.get_valid_token() @@ -62,8 +63,11 @@ class QwenCode(OpenaiTemplate): api_base=creds.get("endpoint"), **kwargs ): - if chunk != last_chunk: + if isinstance(chunk, str): + if chunk != last_chunk: + yield chunk + last_chunk = chunk + else: yield chunk - last_chunk = chunk except: raise \ No newline at end of file diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py index 2d854502..66b91045 100644 --- a/g4f/api/__init__.py +++ b/g4f/api/__init__.py @@ -239,8 +239,7 @@ class Api: user_g4f_api_key = await self.get_g4f_api_key(request) except HTTPException: user_g4f_api_key = await self.security(request) - if hasattr(user_g4f_api_key, "credentials"): - user_g4f_api_key = user_g4f_api_key.credentials + user_g4f_api_key = getattr(user_g4f_api_key, "credentials", user_g4f_api_key) if AppConfig.demo and user is None: ip = request.headers.get("X-Forwarded-For", "")[:4].strip(":.") country = request.headers.get("Cf-Ipcountry", "") @@ -265,14 +264,14 @@ class Api: debug.log(f"User: '{user}' G4F API key expires in {hours}h {minutes}m {seconds}s") if expires < 0: return ErrorResponse.from_message("G4F API key expired", HTTP_401_UNAUTHORIZED) + count = 0 + for char in user: + if char.isupper(): + count += 1 + if count > 4: + return ErrorResponse.from_message("Invalid user name (screaming)", HTTP_401_UNAUTHORIZED) else: user = "admin" - count = 0 - for char in string: - if char.isupper(): - count += 1 - if count > 4: - return ErrorResponse.from_message("Invalid user name", HTTP_401_UNAUTHORIZED) path = request.url.path if path.startswith("/v1") or path.startswith("/api/") or (AppConfig.demo and path == '/backend-api/v2/upload_cookies'): if request.method != "OPTIONS" and not path.endswith("/models"): diff --git a/g4f/image/__init__.py b/g4f/image/__init__.py index 96bc1e0b..b9739fbb 100644 --- a/g4f/image/__init__.py +++ b/g4f/image/__init__.py @@ -107,6 +107,11 @@ def is_data_an_media(data, filename: str = None) -> str: return content_type if isinstance(data, bytes): return is_accepted_format(data) + if isinstance(data, str) and data.startswith("http"): + path = urlparse(data).path + extension = get_extension(path) + if extension is not None: + return EXTENSIONS_MAP[extension] return is_data_uri_an_image(data) def is_valid_media(data: ImageType = None, filename: str = None) -> str: