返回提交历史
Modified
.gitignore
+10
-47
Modified
g4f/Provider/needs_auth/LMArena.py
+12
-5
Modified
g4f/Provider/needs_auth/Video.py
+13
-7
Modified
g4f/Provider/qwen/QwenCode.py
+11
-7
Modified
g4f/api/__init__.py
+7
-8
Modified
g4f/image/__init__.py
+5
-0
XFEstudio/gpt4free
Refactor Cloudflare and LMArena providers to enhance authentication handling and improve WebSocket communication
da6c00e2
代码差异
6 个文件
+58
-74
@@ -1,48 +1,11 @@
1
# Default ignored files
2
/shelf/
3
/workspace.xml
4
# Editor-based HTTP Client requests
5
/httpRequests/
6
# Datasource local storage ignored files
7
/dataSources/
8
/dataSources.local.xml
9
10
# Ignore local python virtual environment
11
venv/
12
13
# Ignore streamlit_chat_app.py conversations pickle
14
conversations.pkl
15
*.pkl
16
.idea/
17
**/__pycache__/
18
__pycache__/
19
20
*.log
21
*.pyc
22
*.egg-info/
23
*.egg
24
*.egg-info
25
.DS_Store
26
*~
27
*.gguf
28
.buildozer
29
har_and_cookies
1
bin
2
dist
3
__pycache__
4
generated_media
5
site-packages
6
projects
30
7
node_modules
31
models
32
projects/windows/g4f
33
generated_images/
34
generated_media/
35
projects/windows/
36
37
*.bak
38
*.backup
39
.env
40
g4f.dev/
41
42
# Build artifacts
43
build/
44
dist/
45
*.spec
46
pyproject.toml.bak
47
debian/
48
winget/
8
g4f.egg-info
9
models/models.json
10
pyvenv.cfg
11
lib64
@@ -568,15 +568,22 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
568
568
pass
569
569
elif has_nodriver or cls.share_url is None:
570
570
async def callback(page):
571
element = await page.select('[style="display: grid;"]')
572
if element:
573
await click_trunstile(page, 'document.querySelector(\'[style="display: grid;"]\')')
574
await page.find("Ask anything…", 120)
575
571
button = await page.find("Accept Cookies")
576
572
if button:
577
573
await button.click()
578
574
else:
579
575
debug.log("No 'Accept Cookies' button found, skipping.")
576
await asyncio.sleep(1)
577
textarea = await page.find("Ask anything…")
578
if textarea:
579
await textarea.send_keys("Hello")
580
await asyncio.sleep(1)
581
button = await page.select('[type="submit"]:has([data-sentry-element="ArrowUp"])')
582
if button:
583
await button.click()
584
element = await page.select('[style="display: grid;"]')
585
if element:
586
await click_trunstile(page, 'document.querySelector(\'[style="display: grid;"]\')')
580
587
if not await page.evaluate('document.cookie.indexOf("arena-auth-prod-v1") >= 0'):
581
588
debug.log("No authentication cookie found, trying to authenticate.")
582
589
await page.select('#cf-turnstile', 300)
@@ -628,7 +635,7 @@ class LMArena(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
628
635
elif model in cls.image_models:
629
636
model_id = cls.image_models[model]
630
637
else:
631
raise ModelNotFoundError(f"Model '{model}' is not supported by LMArena Beta.")
638
raise ModelNotFoundError(f"Model '{model}' is not supported by LMArena provider.")
632
639
633
640
userMessageId = str(uuid.uuid7())
634
641
modelAMessageId = str(uuid.uuid7())
@@ -1,5 +1,6 @@
1
1
from __future__ import annotations
2
2
3
import os
3
4
import asyncio
4
5
from typing import Optional
5
6
from aiohttp import ClientSession, ClientTimeout
@@ -10,13 +11,13 @@ from aiohttp import ClientSession
10
11
try:
11
12
import nodriver
12
13
from nodriver.core.connection import ProtocolException
14
has_nodriver = True
13
15
except:
14
pass
16
has_nodriver = False
15
17
16
18
from ...typing import Messages, AsyncResult
17
19
from ...providers.response import VideoResponse, Reasoning, ContinueResponse, ProviderInfo
18
20
from ...requests import get_nodriver
19
from ...errors import MissingRequirementsError
20
21
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
21
22
from ..helper import format_media_prompt
22
23
from ... import debug
@@ -53,7 +54,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
53
54
"sora": "https://sora.chatgpt.com/explore",
54
55
#"veo": "https://aistudio.google.com/generate-video"
55
56
}
56
api_url = f"{PUBLIC_URL}/backend-api/v2/create?provider=Video&cache=true&prompt="
57
api_path = f"?provider=Video&cache=true&prompt="
57
58
drive_url = "https://www.googleapis.com/drive/v3/"
58
59
59
60
active_by_default = True
@@ -62,10 +63,11 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
62
63
video_models = models
63
64
64
65
needs_auth = True
65
working = True
66
working = has_nodriver
66
67
67
68
browser = None
68
69
stop_browser = None
70
share_url: Optional[str] = None
69
71
70
72
@classmethod
71
73
async def create_async_generator(
@@ -77,6 +79,8 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
77
79
aspect_ratio: str = None,
78
80
**kwargs
79
81
) -> AsyncResult:
82
if cls.share_url is None:
83
cls.share_url = os.getenv("G4F_SHARE_URL")
80
84
if not model:
81
85
model = cls.default_model
82
86
if model not in cls.video_models:
@@ -94,10 +98,12 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
94
98
yield Reasoning(label="Open browser")
95
99
browser, stop_browser = await get_nodriver(proxy=proxy)
96
100
except Exception as e:
101
if cls.share_url is None:
102
raise
97
103
debug.error(f"Error getting nodriver:", e)
98
104
async with ClientSession() as session:
99
105
yield Reasoning(label="Generating")
100
async with session.get(cls.api_url + quote(prompt)) as response:
106
async with session.get(f"{cls.share_url}{cls.api_path + quote(prompt)}") as response:
101
107
if not response.ok:
102
108
debug.error(f"Failed to generate Video: {response.status}")
103
109
else:
@@ -108,7 +114,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
108
114
return
109
115
yield VideoResponse(str(response.url), prompt)
110
116
return
111
raise MissingRequirementsError("Video provider requires a browser to be installed.")
117
raise
112
118
page = None
113
119
try:
114
120
yield ContinueResponse("Timeout waiting for Video URL")
@@ -123,7 +129,7 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
123
129
RequestConfig.headers = {}
124
130
for key, value in event.request.headers.items():
125
131
RequestConfig.headers[key.lower()] = value
126
for _, urls in RequestConfig.urls.items():
132
for urls in RequestConfig.urls.values():
127
133
if event.request.url in urls:
128
134
return
129
135
debug.log(f"Adding URL: {event.request.url}")
@@ -14,9 +14,7 @@ class QwenCode(OpenaiTemplate):
14
14
needs_auth = True
15
15
active_by_default = True
16
16
default_model = "qwen3-coder-plus"
17
default_vision_model = "qwen-vl-max-latest"
18
models = [default_model, default_vision_model]
19
vision_models = [default_vision_model]
17
models = [default_model]
20
18
client = QwenContentGenerator(QwenOAuth2Client())
21
19
22
20
@classmethod
@@ -48,9 +46,12 @@ class QwenCode(OpenaiTemplate):
48
46
api_base=creds.get("endpoint", api_base),
49
47
**kwargs
50
48
):
51
if chunk != last_chunk:
49
if isinstance(chunk, str):
50
if chunk != last_chunk:
51
yield chunk
52
last_chunk = chunk
53
else:
52
54
yield chunk
53
last_chunk = chunk
54
55
except TokenManagerError:
55
56
await cls.client.shared_manager.getValidCredentials(cls.client.qwen_client, True)
56
57
creds = await cls.client.get_valid_token()
@@ -62,8 +63,11 @@ class QwenCode(OpenaiTemplate):
62
63
api_base=creds.get("endpoint"),
63
64
**kwargs
64
65
):
65
if chunk != last_chunk:
66
if isinstance(chunk, str):
67
if chunk != last_chunk:
68
yield chunk
69
last_chunk = chunk
70
else:
66
71
yield chunk
67
last_chunk = chunk
68
72
except:
69
73
raise
@@ -239,8 +239,7 @@ class Api:
239
239
user_g4f_api_key = await self.get_g4f_api_key(request)
240
240
except HTTPException:
241
241
user_g4f_api_key = await self.security(request)
242
if hasattr(user_g4f_api_key, "credentials"):
243
user_g4f_api_key = user_g4f_api_key.credentials
242
user_g4f_api_key = getattr(user_g4f_api_key, "credentials", user_g4f_api_key)
244
243
if AppConfig.demo and user is None:
245
244
ip = request.headers.get("X-Forwarded-For", "")[:4].strip(":.")
246
245
country = request.headers.get("Cf-Ipcountry", "")
@@ -265,14 +264,14 @@ class Api:
265
264
debug.log(f"User: '{user}' G4F API key expires in {hours}h {minutes}m {seconds}s")
266
265
if expires < 0:
267
266
return ErrorResponse.from_message("G4F API key expired", HTTP_401_UNAUTHORIZED)
267
count = 0
268
for char in user:
269
if char.isupper():
270
count += 1
271
if count > 4:
272
return ErrorResponse.from_message("Invalid user name (screaming)", HTTP_401_UNAUTHORIZED)
268
273
else:
269
274
user = "admin"
270
count = 0
271
for char in string:
272
if char.isupper():
273
count += 1
274
if count > 4:
275
return ErrorResponse.from_message("Invalid user name", HTTP_401_UNAUTHORIZED)
276
275
path = request.url.path
277
276
if path.startswith("/v1") or path.startswith("/api/") or (AppConfig.demo and path == '/backend-api/v2/upload_cookies'):
278
277
if request.method != "OPTIONS" and not path.endswith("/models"):
@@ -107,6 +107,11 @@ def is_data_an_media(data, filename: str = None) -> str:
107
107
return content_type
108
108
if isinstance(data, bytes):
109
109
return is_accepted_format(data)
110
if isinstance(data, str) and data.startswith("http"):
111
path = urlparse(data).path
112
extension = get_extension(path)
113
if extension is not None:
114
return EXTENSIONS_MAP[extension]
110
115
return is_data_uri_an_image(data)
111
116
112
117
def is_valid_media(data: ImageType = None, filename: str = None) -> str: