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

XFEstudio/gpt4free

Fix generate image in OpenaiChat Add HarProvider, disable LMArenaProvider

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

代码差异

9 个文件 +1675 -42
Modified MANIFEST.in +2 -1
@@ -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 *
Modified g4f/Provider/LMArenaProvider.py +1 -1
@@ -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"}
Modified g4f/Provider/PerplexityLabs.py +1 -0
@@ -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
Modified g4f/Provider/__init__.py +1 -0
@@ -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:
Added g4f/Provider/har/__init__.py +151 -0
@@ -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}"
Modified g4f/Provider/hf_space/CohereForAI_C4AI_Command.py +1 -1
@@ -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
Modified g4f/Provider/needs_auth/OpenaiChat.py +34 -38
@@ -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
Modified g4f/gui/server/backend_api.py +1 -1
@@ -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)