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

XFEstudio/gpt4free

Refactor providers to use CDP-based functionality and remove has_nodriver references

da953e78
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

11 个文件 +51 -68
Modified g4f/Provider/Copilot.py +2 -2
@@ -26,7 +26,7 @@ from ..typing import AsyncResult, Messages, MediaListType
26 26 from ..errors import MissingRequirementsError, NoValidHarFileError, MissingAuthError
27 27 from ..providers.response import *
28 28 from ..tools.media import merge_media
29 from ..requests import get_nodriver, DEFAULT_HEADERS
29 from ..requests import get_nodriver, DEFAULT_HEADERS, has_cdp
30 30 from ..image import to_bytes, is_accepted_format
31 31 from .helper import get_last_user_message
32 32 from ..files import get_bucket_dir
@@ -119,7 +119,7 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
119 119 access_token, useridentitytype, cookies = readHAR(cls.url)
120 120 except NoValidHarFileError as h:
121 121 debug.log(f"Copilot: {h}")
122 if has_nodriver:
122 if has_cdp:
123 123 yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
124 124 (
125 125 access_token,
Modified g4f/Provider/CopilotSession.py +6 -3
@@ -92,9 +92,12 @@ class CopilotSession(AsyncAuthedProvider, ProviderModelMixin):
92 92 await page.send(cdp.network.enable())
93 93 queue = asyncio.Queue()
94 94
95 def handle_ws_message(event):
96 if hasattr(event, "response") and event.response.payload_data:
97 queue.put_nowait((event.request_id, event.response.payload_data))
95 def handle_ws_message(event, **kwargs):
96 response = getattr(event, "response", None)
97 if response is not None:
98 payload_data = getattr(response, "payloadData", None) or response.get("payloadData")
99 if payload_data:
100 queue.put_nowait((event.request_id, payload_data))
98 101
99 102 page.add_handler(cdp.network.WebSocketFrameReceived, handle_ws_message)
100 103 textarea = await page.select("textarea")
Modified g4f/Provider/DeepInfra.py +26 -29
@@ -3,27 +3,25 @@ from __future__ import annotations
3 3 import asyncio
4 4 import requests
5 5
6 from ..requests.cdp import SyncCDPSession
6 from ..requests.cdp import CDPSession
7 7 from .. import debug
8 8 from .template import OpenaiTemplate
9 9
10 10
11 def _get_turnstile_token_sync(model: str) -> str:
11 async def _get_turnstile_token_async(model: str) -> str:
12 12 """
13 Synchronous Turnstile token retrieval using SyncCDPSession with retries.
14 Uses a blocking recv() loop — no async timeouts, waits as long as needed.
15 Designed to be run via asyncio.run_in_executor() from async context.
13 Async Turnstile token retrieval using CDPSession with retries.
14 Navigates to the DeepInfra model page, injects a fetch blocker,
15 enters text, and polls for the Cloudflare Turnstile token.
16 16 """
17 import time
18
19 17 for attempt in range(1):
20 session = SyncCDPSession()
21 session.start_chrome()
18 session = CDPSession()
19 await session.start()
22 20
23 21 try:
24 22 url = f"https://deepinfra.com/{model}"
25 23 debug.log(f"[DeepInfra] Navigating to {url} (Attempt {attempt + 1}/3)...")
26 session.navigate(url)
24 await session.navigate(url)
27 25
28 26 # Inject completions request blocker
29 27 fetch_blocker_js = """
@@ -36,10 +34,10 @@ def _get_turnstile_token_sync(model: str) -> str:
36 34 return origFetch.apply(this, args);
37 35 };
38 36 """
39 session.evaluate_js(fetch_blocker_js)
37 await session.evaluate_js(fetch_blocker_js)
40 38
41 39 # Try to click "Accept" on cookies consent popup if present
42 session.evaluate_js(
40 await session.evaluate_js(
43 41 """
44 42 (() => {
45 43 const btn = Array.from(document.querySelectorAll('button')).find(b => b.textContent.trim() === 'Accept');
@@ -50,14 +48,14 @@ def _get_turnstile_token_sync(model: str) -> str:
50 48
51 49 # Click on an empty page area to give window focus — signals Cloudflare that
52 50 # a real user is present, which speeds up Turnstile token generation significantly.
53 session.click(200, 400)
51 await session.click(200, 400)
54 52
55 53 # Wait for textarea readiness, then focus and input text
56 54 debug.log("[DeepInfra] Waiting for active textarea...")
57 55 text_entered = False
58 56 for _ in range(80): # Up to 40 seconds
59 57 try:
60 ready = session.evaluate_js(
58 ready = await session.evaluate_js(
61 59 """
62 60 (() => {
63 61 const ta = document.querySelector('textarea');
@@ -79,14 +77,14 @@ def _get_turnstile_token_sync(model: str) -> str:
79 77 )
80 78
81 79 # Retrieve textarea nodeId for native focusing
82 doc = session.call("DOM.getDocument")
80 doc = await session.call("DOM.getDocument")
83 81 root_id = doc["root"]["nodeId"]
84 textarea = session.call(
82 textarea = await session.call(
85 83 "DOM.querySelector", nodeId=root_id, selector="textarea"
86 84 )
87 85
88 86 # Native focus via CDP
89 session.call("DOM.focus", nodeId=textarea["nodeId"])
87 await session.call("DOM.focus", nodeId=textarea["nodeId"])
90 88
91 89 # Enter text via native CDP command
92 90 import random
@@ -102,12 +100,12 @@ def _get_turnstile_token_sync(model: str) -> str:
102 100 "Can you hear me?",
103 101 ]
104 102 )
105 session.call("Input.insertText", text=test_prompt)
103 await session.call("Input.insertText", text=test_prompt)
106 104
107 time.sleep(0.5)
105 await asyncio.sleep(0.5)
108 106
109 107 # Simulate Enter keypress
110 session.call(
108 await session.call(
111 109 "Input.dispatchKeyEvent",
112 110 type="keyDown",
113 111 windowsVirtualKeyCode=13,
@@ -116,7 +114,7 @@ def _get_turnstile_token_sync(model: str) -> str:
116 114 text="\r",
117 115 unmodifiedText="\r",
118 116 )
119 session.call(
117 await session.call(
120 118 "Input.dispatchKeyEvent",
121 119 type="keyUp",
122 120 windowsVirtualKeyCode=13,
@@ -130,13 +128,13 @@ def _get_turnstile_token_sync(model: str) -> str:
130 128 break
131 129 except Exception:
132 130 pass
133 time.sleep(0.5)
131 await asyncio.sleep(0.5)
134 132
135 133 if not text_entered:
136 134 debug.log(
137 135 "[DeepInfra] Textarea/Turnstile not ready or failed to submit, retrying attempt..."
138 136 )
139 session.close()
137 await session.close()
140 138 continue
141 139
142 140 # Poll page for Turnstile token
@@ -145,28 +143,27 @@ def _get_turnstile_token_sync(model: str) -> str:
145 143 token = ""
146 144 for i in range(240): # Up to 120 seconds per attempt
147 145 try:
148 token = session.evaluate_js(token_js)
146 token = await session.evaluate_js(token_js)
149 147 if token:
150 148 debug.log(f"[DeepInfra] Token generated on check {i+1}!")
151 149 return token
152 150 except Exception:
153 151 pass
154 time.sleep(0.5)
152 await asyncio.sleep(0.5)
155 153
156 154 except Exception as e:
157 155 debug.log(f"[DeepInfra] Error on attempt {attempt + 1}: {e}")
158 156 finally:
159 session.close()
157 await session.close()
160 158
161 159 return ""
162 160
163 161
164 162 async def get_turnstile_token_async(model: str = None) -> str:
165 """Run the synchronous Turnstile solver in a thread pool executor."""
163 """Obtain a Cloudflare Turnstile token for DeepInfra."""
166 164 if not model:
167 165 model = DeepInfra.default_model
168 loop = asyncio.get_running_loop()
169 return await loop.run_in_executor(None, _get_turnstile_token_sync, model)
166 return await _get_turnstile_token_async(model)
170 167
171 168
172 169 class DeepInfra(OpenaiTemplate):
Modified g4f/Provider/audio/ElevenLabs.py +2 -4
@@ -4,8 +4,6 @@ import os
4 4 import json
5 5 import base64
6 6
7 from ...requests import has_nodriver
8
9 7 from ...typing import AsyncResult, Messages
10 8 from ...providers.response import AudioResponse
11 9 from ...image.copy_images import get_filename, get_media_dir, ensure_media_dir
@@ -35,8 +33,8 @@ class ElevenLabs(AsyncGeneratorProvider, ProviderModelMixin):
35 33
36 34 label = "ElevenLabs TTS"
37 35 url = "https://elevenlabs.io"
38 working = has_nodriver
39 use_nodriver = has_nodriver
36 working = True
37 use_nodriver = True
40 38 needs_auth = True # hcaptcha accessibility cookie
41 39
42 40 model_id = "elevenlabs-tts"
Modified g4f/Provider/glm/captcha_solver.py +2 -2
@@ -26,7 +26,7 @@ from typing import Optional
26 26
27 27 from ... import debug
28 28
29 from ...requests import get_nodriver, has_nodriver
29 from ...requests import get_nodriver
30 30 from ...requests.cdp_browser import cdp
31 31
32 32
@@ -330,4 +330,4 @@ def invalidate_captcha_token() -> None:
330 330
331 331 def is_available() -> bool:
332 332 """Whether the captcha solver can run (requires zendriver)."""
333 return has_nodriver
333 return True
Modified g4f/Provider/needs_auth/Gemini.py +0 -3
@@ -357,9 +357,6 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
357 357
358 358 @classmethod
359 359 async def login_generator(cls, proxy: str = None) -> AsyncIterator[str]:
360 if not has_nodriver:
361 debug.log("Skip browser login in Gemini provider")
362 return
363 360 browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="gemini")
364 361 try:
365 362 yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
Modified g4f/Provider/needs_auth/LMArena.py +7 -12
@@ -377,7 +377,7 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
377 377 return
378 378 if isinstance(data, dict):
379 379 json_data = data
380 elif data[0] == "$":
380 elif data and data[0] == "$":
381 381 if data[1] in ["div", "defs", "style", "script"]:
382 382 return
383 383 json_data = data[3]
@@ -405,7 +405,10 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
405 405 continue
406 406 chunk_id, chunk_data = match.groups()
407 407 if chunk_data.startswith("I["):
408 data = json.loads(chunk_data[1:])
408 try:
409 data = json.loads(chunk_data[1:])
410 except json.decoder.JSONDecodeError:
411 continue
409 412 async with StreamSession() as session:
410 413 if "Evaluation" == data[2]:
411 414 js_files = dict(zip(data[1][::2], data[1][1::2]))
@@ -560,12 +563,8 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
560 563 for _ in range(2):
561 564 if args:
562 565 pass
563 elif has_nodriver:
564 args = await cls.get_args_from_nodriver(proxy, _need_clear_cookies)
565 566 else:
566 raise MissingRequirementsError(
567 "No auth file found and browser (CDP) is not available."
568 )
567 args = await cls.get_args_from_nodriver(proxy, _need_clear_cookies)
569 568
570 569 if not cls._models_loaded:
571 570 # change to async
@@ -573,10 +572,6 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
573 572
574 573 def get_mode_id(_model):
575 574 model_id = None
576 # if not model:
577 # model = cls.default_model
578 if _model in cls.model_aliases:
579 _model = cls.model_aliases[_model]
580 575 if _model in cls.text_models:
581 576 model_id = cls.text_models[_model]
582 577 elif _model in cls.image_models:
@@ -609,7 +604,7 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
609 604 userMessageId = str(uuid7())
610 605 modelAMessageId = str(uuid7())
611 606 modelBMessageId = str(uuid7())
612 if not cls._grecaptcha and has_nodriver:
607 if not cls._grecaptcha:
613 608 debug.log("No grecaptcha token found, obtaining new one...")
614 609 args = await cls.get_grecaptcha(args, proxy)
615 610 files = await cls.prepare_images(args, media)
Modified g4f/Provider/needs_auth/OpenaiChat.py +5 -6
@@ -1352,12 +1352,11 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
1352 1352 f"Access token is not valid: {cls.request_config.access_token}"
1353 1353 )
1354 1354 except NoValidHarFileError:
1355 if has_nodriver:
1356 if cls.request_config.access_token is None:
1357 yield RequestLogin(
1358 cls.label, os.environ.get("G4F_LOGIN_URL", "")
1359 )
1360 await cls.nodriver_auth(proxy)
1355 if cls.request_config.access_token is None:
1356 yield RequestLogin(
1357 cls.label, os.environ.get("G4F_LOGIN_URL", "")
1358 )
1359 await cls.nodriver_auth(proxy)
1361 1360 else:
1362 1361 raise
1363 1362
Modified g4f/api/__init__.py +1 -1
@@ -73,7 +73,7 @@ except ImportError:
73 73 pass
74 74
75 75
76 from g4f.requests import has_nodriver, has_cdp
76 from g4f.requests import has_cdp
77 77
78 78 import g4f
79 79 import g4f.debug
Modified g4f/gui/server/api.py +0 -1
@@ -270,7 +270,6 @@ class Api:
270 270 },
271 271 )
272 272 for chunk in result:
273 print(type(chunk))
274 273 if isinstance(chunk, ProviderInfo):
275 274 model = getattr(chunk, "model", model)
276 275 provider = getattr(chunk, "provider", provider)
Modified g4f/requests/__init__.py +0 -5
@@ -41,11 +41,6 @@ try:
41 41 except ImportError:
42 42 has_cdp = False
43 43
44 # CDP-based browser wrapper replaces zendriver/nodriver entirely.
45 # ``has_nodriver`` is kept as an alias for ``has_cdp`` so existing provider
46 # code that checks ``has_nodriver`` continues to work without modification.
47 has_nodriver = has_cdp
48
49 44 from .cdp_browser import (
50 45 CDPBrowser,
51 46 CDPTab,