返回提交历史
Modified
MANIFEST.in
+2
-1
Modified
g4f/Provider/LMArenaProvider.py
+1
-1
Modified
g4f/Provider/PerplexityLabs.py
+1
-0
Modified
g4f/Provider/__init__.py
+1
-0
Added
g4f/Provider/har/__init__.py
+151
-0
Added
g4f/Provider/har/lmarena.ai.har
+1483
-0
Modified
g4f/Provider/hf_space/CohereForAI_C4AI_Command.py
+1
-1
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+34
-38
Modified
g4f/gui/server/backend_api.py
+1
-1
XFEstudio/gpt4free
Fix generate image in OpenaiChat Add HarProvider, disable LMArenaProvider
9aba6273
代码差异
9 个文件
+1675
-42
@@ -2,4 +2,5 @@ recursive-include g4f/gui/server *
2
2
recursive-include g4f/gui/client *
3
3
recursive-include g4f/Provider/npm *
4
4
recursive-include g4f/Provider/gigachat_crt *
5
recursive-include g4f/Provider/you *
5
recursive-include g4f/Provider/you *
6
recursive-include g4f/Provider/har *
@@ -17,7 +17,7 @@ class LMArenaProvider(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin)
17
17
label = "LM Arena"
18
18
url = "https://lmarena.ai"
19
19
api_endpoint = "/queue/join?"
20
working = True
20
working = False
21
21
22
22
default_model = "gpt-4o"
23
23
model_aliases = {default_model: "chatgpt-4o-latest-20250326"}
@@ -13,6 +13,7 @@ API_URL = "https://www.perplexity.ai/socket.io/"
13
13
WS_URL = "wss://www.perplexity.ai/socket.io/"
14
14
15
15
class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
16
label = "Perplexity Labs"
16
17
url = "https://labs.perplexity.ai"
17
18
working = True
18
19
@@ -12,6 +12,7 @@ except ImportError as e:
12
12
from .needs_auth import *
13
13
from .template import OpenaiTemplate, BackendApi
14
14
from .hf import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference, HuggingFaceMedia
15
from .har import HarProvider
15
16
try:
16
17
from .not_working import *
17
18
except ImportError as e:
@@ -0,0 +1,151 @@
1
from __future__ import annotations
2
3
import os
4
import json
5
import uuid
6
from urllib.parse import urlparse
7
8
from ...typing import AsyncResult, Messages
9
from ...requests import StreamSession, raise_for_status
10
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
11
from ..helper import get_last_user_message
12
from ..openai.har_file import get_headers
13
14
class HarProvider(AsyncGeneratorProvider, ProviderModelMixin):
15
url = "https://lmarena.ai"
16
working = True
17
18
@classmethod
19
def get_models(cls):
20
for harFile in read_har_files():
21
for v in harFile['log']['entries']:
22
request_url = v['request']['url']
23
if not request_url.startswith(cls.url) or "." in urlparse(request_url).path or "heartbeat" in request_url:
24
continue
25
if "\n\ndata: " not in v['response']['content']['text']:
26
continue
27
chunk = v['response']['content']['text'].split("\n\ndata: ")[2]
28
cls.models = list(dict.fromkeys(get_str_list(find_list(json.loads(chunk), 'choices'))).keys())
29
if cls.models:
30
break
31
return cls.models
32
33
@classmethod
34
async def create_async_generator(
35
cls, model: str, messages: Messages,
36
proxy: str = None,
37
**kwargs
38
) -> AsyncResult:
39
if model in cls.model_aliases:
40
model = cls.model_aliases[model]
41
session_hash = str(uuid.uuid4()).replace("-", "")
42
prompt = get_last_user_message(messages)
43
44
for harFile in read_har_files():
45
async with StreamSession(impersonate="chrome") as session:
46
for v in harFile['log']['entries']:
47
request_url = v['request']['url']
48
if not request_url.startswith(cls.url) or "." in urlparse(request_url).path or "heartbeat" in request_url:
49
continue
50
postData = None
51
if "postData" in v['request']:
52
postData = v['request']['postData']['text']
53
postData = postData.replace('"hello"', json.dumps(prompt))
54
postData = postData.replace("__SESSION__", session_hash)
55
if model:
56
postData = postData.replace("__MODEL__", model)
57
request_url = request_url.replace("__SESSION__", session_hash)
58
method = v['request']['method'].lower()
59
60
async with getattr(session, method)(request_url, data=postData, headers=get_headers(v), proxy=proxy) as response:
61
await raise_for_status(response)
62
if "heartbeat" in request_url:
63
continue
64
returned_data = ""
65
async for line in response.iter_lines():
66
if not line.startswith(b"data: "):
67
continue
68
for content in find_str(json.loads(line[6:]), 3):
69
if content == '<span class="cursor"></span> ' or content == 'update':
70
continue
71
if content.endswith("▌"):
72
content = content[:-2]
73
new_content = content
74
if content.startswith(returned_data):
75
new_content = content[len(returned_data):]
76
if not new_content:
77
continue
78
returned_data += new_content
79
yield new_content
80
81
def read_har_files():
82
for root, _, files in os.walk(os.path.dirname(__file__)):
83
for file in files:
84
if not file.endswith(".har"):
85
continue
86
with open(os.path.join(root, file), 'rb') as file:
87
try:
88
yield json.loads(file.read())
89
except json.JSONDecodeError:
90
raise RuntimeError(f"Failed to read HAR file: {file}")
91
92
def read_str_recusive(data):
93
if isinstance(data, dict):
94
data = data.values()
95
for item in data:
96
if isinstance(item, (list, dict)):
97
yield from read_str_recusive(item)
98
elif isinstance(item, str):
99
yield item
100
101
def find_str(data, skip=0):
102
for item in read_str_recusive(data):
103
if skip > 0:
104
skip -= 1
105
continue
106
yield item
107
break
108
109
def read_list_recusive(data, key):
110
if isinstance(data, dict):
111
for k, v in data.items():
112
if k == key:
113
print(k, v)
114
yield v
115
else:
116
yield from read_list_recusive(v, key)
117
elif isinstance(data, list):
118
for item in data:
119
yield from read_list_recusive(item, key)
120
121
def find_list(data, key):
122
for item in read_list_recusive(data, key):
123
if isinstance(item, str):
124
yield item
125
elif isinstance(item, list):
126
for sub_item in item:
127
yield sub_item
128
129
def get_str_list(data):
130
for item in data:
131
if isinstance(item, list):
132
yield from get_str_list(item)
133
else:
134
yield item
135
136
# with open("g4f/Provider/har/lmarena.ai.har", "r") as f:
137
# try:
138
# harFile = json.loads(f.read())
139
# except json.JSONDecodeError:
140
# raise RuntimeError(f"Failed to read HAR file")
141
142
# new_entries = []
143
# for v in harFile['log']['entries']:
144
# request_url = v['request']['url']
145
# if not request_url.startswith("https://lmarena.ai") or "." in urlparse(request_url).path or "heartbeat" in request_url:
146
# continue
147
# v['request']['cookies'] = []
148
# v['request']['headers'] = [header for header in v['request']['headers'] if header['name'].lower() != "cookie"]
149
# v['response']['headers'] = []
150
# new_entries.append(v)
151
# print(f"Request URL: {request_url}"
@@ -11,7 +11,7 @@ from ...providers.response import JsonConversation, TitleGeneration
11
11
12
12
class CohereForAI_C4AI_Command(AsyncGeneratorProvider, ProviderModelMixin):
13
13
label = "CohereForAI C4AI Command"
14
url = "https://cohereforai-c4ai-command.hf.space"
14
url = " https://coherelabs-c4ai-command.hf.space"
15
15
conversation_url = f"{url}/conversation"
16
16
17
17
working = True
@@ -24,10 +24,10 @@ from ...requests import StreamSession
24
24
from ...requests import get_nodriver
25
25
from ...image import ImageRequest, to_image, to_bytes, is_accepted_format
26
26
from ...errors import MissingAuthError, NoValidHarFileError
27
from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult, ImageResponse
27
from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult, ImageResponse, ImagePreview
28
28
from ...providers.response import Sources, TitleGeneration, RequestLogin, Reasoning
29
29
from ...tools.media import merge_media
30
from ..helper import format_cookies, get_last_user_message
30
from ..helper import format_cookies, format_image_prompt
31
31
from ..openai.models import default_model, default_image_model, models, image_models, text_models
32
32
from ..openai.har_file import get_request_config
33
33
from ..openai.har_file import RequestConfig, arkReq, arkose_url, start_url, conversation_url, backend_url, backend_anon_url
@@ -254,31 +254,26 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
254
254
return messages
255
255
256
256
@classmethod
257
async def get_generated_image(cls, session: StreamSession, auth_result: AuthResult, element: dict, prompt: str, conversation_id: str) -> ImageResponse:
258
try:
259
prompt = element["metadata"]["dalle"]["prompt"]
260
except IndexError:
261
pass
262
try:
263
file_id = element["asset_pointer"]
264
if "file-service://" in file_id:
265
file_id = file_id.split("file-service://", 1)[-1]
266
url = f"{cls.url}/backend-api/files/{file_id}/download"
267
else:
268
file_id = file_id.split("sediment://")[-1]
269
url = f"{cls.url}/backend-api/conversation/{conversation_id}/attachment/{file_id}/download"
270
except TypeError:
271
return
272
except Exception as e:
273
raise RuntimeError(f"No Image: {element} - {e}")
274
try:
257
async def get_generated_images(cls, session: StreamSession, auth_result: AuthResult, parts: list, prompt: str, conversation_id: str) -> AsyncIterator:
258
download_urls = []
259
for element in [parts] if isinstance(parts, str) else parts:
260
if isinstance(element, dict) and element.get("content_type") == "image_asset_pointer":
261
if not prompt:
262
prompt = element["metadata"]["dalle"]["prompt"]
263
element = element["asset_pointer"]
264
element = element.split("sediment://")[-1]
265
url = f"{cls.url}/backend-api/conversation/{conversation_id}/attachment/{element}/download"
266
debug.log(f"OpenaiChat: Downloading image: {url}")
275
267
async with session.get(url, headers=auth_result.headers) as response:
276
268
cls._update_request_args(auth_result, session)
277
269
await raise_for_status(response)
278
download_url = (await response.json())["download_url"]
279
return ImageResponse(download_url, prompt)
280
except Exception as e:
281
raise RuntimeError(f"Error in downloading image: {e}")
270
data = await response.json()
271
download_url = data.get("download_url")
272
if download_url is None:
273
print(data)
274
else:
275
download_urls.append(download_url)
276
return ImagePreview(download_urls, prompt)
282
277
283
278
@classmethod
284
279
async def create_authed(
@@ -394,10 +389,8 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
394
389
#f"Proofofwork: {'False' if proofofwork is None else proofofwork[:12]+'...'}",
395
390
#f"AccessToken: {'False' if cls._api_key is None else cls._api_key[:12]+'...'}",
396
391
)]
397
if action is None or action == "variant" or action == "continue" and conversation.message_id is None:
398
action = "next"
399
392
data = {
400
"action": action,
393
"action": "next",
401
394
"parent_message_id": conversation.message_id,
402
395
"model": model,
403
396
"timezone_offset_min":-60,
@@ -413,7 +406,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
413
406
if conversation.conversation_id is not None:
414
407
data["conversation_id"] = conversation.conversation_id
415
408
debug.log(f"OpenaiChat: Use conversation: {conversation.conversation_id}")
416
prompt = get_last_user_message(messages) if prompt is None else prompt
409
conversation.prompt = format_image_prompt(messages, prompt)
417
410
if action != "continue":
418
411
data["parent_message_id"] = getattr(conversation, "parent_message_id", conversation.message_id)
419
412
conversation.parent_message_id = None
@@ -444,7 +437,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
444
437
await raise_for_status(response)
445
438
buffer = u""
446
439
async for line in response.iter_lines():
447
async for chunk in cls.iter_messages_line(session, auth_result, line, conversation, sources, prompt):
440
async for chunk in cls.iter_messages_line(session, auth_result, line, conversation, sources):
448
441
if isinstance(chunk, str):
449
442
chunk = chunk.replace("\ue203", "").replace("\ue204", "").replace("\ue206", "")
450
443
buffer += chunk
@@ -469,6 +462,10 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
469
462
break
470
463
if sources.list:
471
464
yield sources
465
if conversation.generated_images:
466
yield ImageResponse(conversation.generated_images.urls, conversation.prompt)
467
conversation.generated_images = None
468
conversation.prompt = None
472
469
if return_conversation:
473
470
yield conversation
474
471
if auth_result.api_key is not None:
@@ -486,7 +483,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
486
483
yield FinishReason(conversation.finish_reason)
487
484
488
485
@classmethod
489
async def iter_messages_line(cls, session: StreamSession, auth_result: AuthResult, line: bytes, fields: Conversation, sources: Sources, prompt: str) -> AsyncIterator:
486
async def iter_messages_line(cls, session: StreamSession, auth_result: AuthResult, line: bytes, fields: Conversation, sources: Sources) -> AsyncIterator:
490
487
if not line.startswith(b"data: "):
491
488
return
492
489
elif line.startswith(b"data: [DONE]"):
@@ -519,6 +516,10 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
519
516
for m in v:
520
517
if m.get("p") == "/message/content/parts/0" and fields.recipient == "all":
521
518
yield m.get("v")
519
elif m.get("p") == "/message/metadata/image_gen_title":
520
fields.prompt = m.get("v")
521
elif m.get("p") == "/message/content/parts/0/asset_pointer":
522
fields.generated_images = await cls.get_generated_images(session, auth_result, m.get("v"), fields.prompt, fields.conversation_id)
522
523
elif m.get("p") == "/message/metadata/search_result_groups":
523
524
for entry in [p.get("entries") for p in m.get("v")]:
524
525
for link in entry:
@@ -547,14 +548,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
547
548
fields.is_thinking = True
548
549
yield Reasoning(status=m.get("metadata", {}).get("initial_text"))
549
550
if c.get("content_type") == "multimodal_text":
550
generated_images = []
551
for element in c.get("parts"):
552
if isinstance(element, dict) and element.get("content_type") == "image_asset_pointer":
553
image = cls.get_generated_image(session, auth_result, element, prompt, fields.conversation_id)
554
generated_images.append(image)
555
for image_response in await asyncio.gather(*generated_images):
556
if image_response is not None:
557
yield image_response
551
yield await cls.get_generated_images(session, auth_result, c.get("parts"), fields.prompt, fields.conversation_id)
558
552
if m.get("author", {}).get("role") == "assistant":
559
553
if fields.parent_message_id is None:
560
554
fields.parent_message_id = v.get("message", {}).get("id")
@@ -738,6 +732,8 @@ class Conversation(JsonConversation):
738
732
self.is_thinking = is_thinking
739
733
self.p = None
740
734
self.thoughts_summary = ""
735
self.prompt = None
736
self.generated_images: ImagePreview = None
741
737
742
738
def get_cookies(
743
739
urls: Optional[Iterator[str]] = None
@@ -396,7 +396,7 @@ class Backend_Api(Api):
396
396
updated = chat_data.get("updated", 0)
397
397
cache_value = self.chat_cache.get(share_id, 0)
398
398
if updated == cache_value:
399
return jsonify({"error": {"message": "invalid date"}}), 400
399
return {"share_id": share_id}
400
400
share_id = secure_filename(share_id)
401
401
bucket_dir = get_bucket_dir(share_id)
402
402
os.makedirs(bucket_dir, exist_ok=True)