from __future__ import annotations
import os
import json
import uuid
import random
import asyncio
from urllib.parse import urlparse
from ....typing import AsyncResult, Messages, MediaListType
from ....requests import DEFAULT_HEADERS, StreamSession, StreamResponse, FormData, raise_for_status
from ....providers.response import JsonConversation, AuthResult
from ....requests import get_args_from_nodriver, has_nodriver
from ....tools.media import merge_media
from ....image import to_bytes, is_accepted_format
from ....errors import ResponseError
from ...base_provider import AsyncAuthedProvider, ProviderModelMixin
from ...helper import get_last_user_message
from ...deprecated.LegacyLMArena import LegacyLMArena
from .... import debug
class HarProvider(AsyncAuthedProvider, ProviderModelMixin):
label = "LMArena (Har)"
url = "https://legacy.lmarena.ai"
api_endpoint = "/queue/join?"
working = False
active_by_default = True
default_model = LegacyLMArena.default_model
@classmethod
async def on_auth_async(cls, proxy: str = None, **kwargs):
if has_nodriver:
try:
async def callback(page):
while not await page.evaluate('document.querySelector(\'textarea[data-testid="textbox"]\')'):
await asyncio.sleep(1)
args = await get_args_from_nodriver(cls.url, proxy=proxy, callback=callback)
except (RuntimeError, FileNotFoundError) as e:
debug.log(f"Nodriver is not available:", e)
args = {"headers": DEFAULT_HEADERS.copy(), "cookies": {}, "impersonate": "chrome"}
else:
args = {"headers": DEFAULT_HEADERS.copy(), "cookies": {}, "impersonate": "chrome"}
args["headers"].update({
"content-type": "application/json",
"accept": "application/json",
"referer": f"{cls.url}/",
"origin": cls.url,
})
yield AuthResult(**args)
@classmethod
def get_models(cls) -> list[str]:
LegacyLMArena.get_models()
cls.models = LegacyLMArena.models
cls.model_aliases = LegacyLMArena.model_aliases
cls.vision_models = LegacyLMArena.vision_models
return cls.models
@classmethod
def _build_second_payloads(cls, model_id: str, session_hash: str, text: str, max_tokens: int, temperature: float, top_p: float):
first_payload = {
"data":[None,model_id,text,{
"text_models":[model_id],
"all_text_models":[model_id],
"vision_models":[],
"image_gen_models":[],
"all_image_gen_models":[],
"search_models":[],
"all_search_models":[],
"models":[model_id],
"all_models":[model_id],
"arena_type":"text-arena"}],
"event_data": None,
"fn_index": 122,
"trigger_id": 157,
"session_hash": session_hash
}
second_payload = {
"data": [],
"event_data": None,
"fn_index": 123,
"trigger_id": 157,
"session_hash": session_hash
}
third_payload = {
"data": [None, temperature, top_p, max_tokens],
"event_data": None,
"fn_index": 124,
"trigger_id": 157,
"session_hash": session_hash
}
return first_payload, second_payload, third_payload
@classmethod
async def create_authed(
cls,
model: str,
messages: Messages,
auth_result: AuthResult,
media: MediaListType = None,
max_tokens: int = 2048,
temperature: float = 0.7,
top_p: float = 1,
conversation: JsonConversation = None,
**kwargs
) -> AsyncResult:
async def read_response(response: StreamResponse):
returned_data = ""
async for line in response.iter_lines():
if not line.startswith(b"data: "):
continue
for content in find_str(json.loads(line[6:]), 3):
if "**NETWORK ERROR DUE TO HIGH TRAFFIC." in content:
raise ResponseError(content)
if content == '<span class="cursor"></span> ' or content == 'update':
continue
if content.endswith("▌"):
content = content[:-2]
new_content = content
if content.startswith(returned_data):
new_content = content[len(returned_data):]
if not new_content:
continue
returned_data += new_content
yield new_content
if model in cls.model_aliases:
model = cls.model_aliases[model]
if isinstance(model, list):
model = random.choice(model)
prompt = get_last_user_message(messages)
async with StreamSession(**auth_result.get_dict()) as session:
if conversation is None:
conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace("-", ""))
media = list(merge_media(media, messages))
if media:
data = FormData()
for i in range(len(media)):
media[i] = (to_bytes(media[i][0]), media[i][1])
for image, image_name in media:
data.add_field(f"files", image, filename=image_name)
async with session.post(f"{cls.url}/upload", params={"upload_id": conversation.session_hash}, data=data) as response:
await raise_for_status(response)
image_files = await response.json()
media = [{
"path": image_file,
"url": f"{cls.url}/file={image_file}",
"orig_name": media[i][1],
"size": len(media[i][0]),
"mime_type": is_accepted_format(media[i][0]),
"meta": {
"_type": "gradio.FileData"
}
} for i, image_file in enumerate(image_files)]
for domain, harFile in read_har_files():
for v in harFile['log']['entries']:
request_url = v['request']['url']
if domain not in request_url or "." in urlparse(request_url).path or "heartbeat" in request_url:
continue
postData = None
if "postData" in v['request']:
postData = v['request']['postData']['text']
postData = postData.replace('"hello"', json.dumps(prompt))
postData = postData.replace('[null,0.7,1,2048]', json.dumps([None, temperature, top_p, max_tokens]))
postData = postData.replace('"files":[]', f'"files":{json.dumps(media)}')
postData = postData.replace("__SESSION__", conversation.session_hash)
if model:
postData = postData.replace("__MODEL__", model)
request_url = request_url.replace("__SESSION__", conversation.session_hash)
method = v['request']['method'].lower()
async with getattr(session, method)(request_url, data=postData) as response:
await raise_for_status(response)
async for chunk in read_response(response):
yield chunk
yield conversation
else:
first_payload, second_payload, third_payload = cls._build_second_payloads(model, conversation.session_hash, prompt, max_tokens, temperature, top_p)
# POST 1
async with session.post(f"{cls.url}{cls.api_endpoint}", json=first_payload) as response:
await raise_for_status(response)
# POST 2
async with session.post(f"{cls.url}{cls.api_endpoint}", json=second_payload) as response:
await raise_for_status(response)
# POST 3
async with session.post(f"{cls.url}{cls.api_endpoint}", json=third_payload) as response:
await raise_for_status(response)
stream_url = f"{cls.url}/queue/data?session_hash={conversation.session_hash}"
async with session.get(stream_url, headers={"Accept": "text/event-stream"}) as response:
await raise_for_status(response)
async for chunk in read_response(response):
yield chunk
def read_har_files():
for root, _, files in os.walk(os.path.dirname(__file__)):
for file in files:
if not file.endswith(".har"):
continue
with open(os.path.join(root, file), 'rb') as f:
try:
yield os.path.splitext(file)[0], json.load(f)
except json.JSONDecodeError:
raise RuntimeError(f"Failed to read HAR file: {file}")
def read_str_recusive(data):
if isinstance(data, dict):
data = data.values()
for item in data:
if isinstance(item, (list, dict)):
yield from read_str_recusive(item)
elif isinstance(item, str):
yield item
def find_str(data, skip: int = 0):
for item in read_str_recusive(data):
if skip > 0:
skip -= 1
continue
yield item
break
def read_list_recusive(data, key):
if isinstance(data, dict):
for k, v in data.items():
if k == key:
yield v
else:
yield from read_list_recusive(v, key)
elif isinstance(data, list):
for item in data:
yield from read_list_recusive(item, key)
def find_list(data, key):
for item in read_list_recusive(data, key):
if isinstance(item, str):
yield item
elif isinstance(item, list):
yield from item
def get_str_list(data):
for item in data:
if isinstance(item, list):
yield from get_str_list(item)
else:
yield item
# with open("g4f/Provider/har/lmarena.ai.har", "r") as f:
# try:
# harFile = json.loads(f.read())
# except json.JSONDecodeError:
# raise RuntimeError(f"Failed to read HAR file")
# new_entries = []
# for v in harFile['log']['entries']:
# request_url = v['request']['url']
# if not request_url.startswith("https://lmarena.ai") or "." in urlparse(request_url).path or "heartbeat" in request_url:
# continue
# v['request']['cookies'] = []
# v['request']['headers'] = [header for header in v['request']['headers'] if header['name'].lower() != "cookie"]
# v['response']['headers'] = []
# new_entries.append(v)
# print(f"Request URL: {request_url}"
from __future__ import annotations
import os
import json
import uuid
import random
import asyncio
from urllib.parse import urlparse
from ....typing import AsyncResult, Messages, MediaListType
from ....requests import DEFAULT_HEADERS, StreamSession, StreamResponse, FormData, raise_for_status
from ....providers.response import JsonConversation, AuthResult
from ....requests import get_args_from_nodriver, has_nodriver
from ....tools.media import merge_media
from ....image import to_bytes, is_accepted_format
from ....errors import ResponseError
from ...base_provider import AsyncAuthedProvider, ProviderModelMixin
from ...helper import get_last_user_message
from ...deprecated.LegacyLMArena import LegacyLMArena
from .... import debug
class HarProvider(AsyncAuthedProvider, ProviderModelMixin):
label = "LMArena (Har)"
url = "https://legacy.lmarena.ai"
api_endpoint = "/queue/join?"
working = False
active_by_default = True
default_model = LegacyLMArena.default_model
@classmethod
async def on_auth_async(cls, proxy: str = None, **kwargs):
if has_nodriver:
try:
async def callback(page):
while not await page.evaluate('document.querySelector(\'textarea[data-testid="textbox"]\')'):
await asyncio.sleep(1)
args = await get_args_from_nodriver(cls.url, proxy=proxy, callback=callback)
except (RuntimeError, FileNotFoundError) as e:
debug.log(f"Nodriver is not available:", e)
args = {"headers": DEFAULT_HEADERS.copy(), "cookies": {}, "impersonate": "chrome"}
else:
args = {"headers": DEFAULT_HEADERS.copy(), "cookies": {}, "impersonate": "chrome"}
args["headers"].update({
"content-type": "application/json",
"accept": "application/json",
"referer": f"{cls.url}/",
"origin": cls.url,
})
yield AuthResult(**args)
@classmethod
def get_models(cls) -> list[str]:
LegacyLMArena.get_models()
cls.models = LegacyLMArena.models
cls.model_aliases = LegacyLMArena.model_aliases
cls.vision_models = LegacyLMArena.vision_models
return cls.models
@classmethod
def _build_second_payloads(cls, model_id: str, session_hash: str, text: str, max_tokens: int, temperature: float, top_p: float):
first_payload = {
"data":[None,model_id,text,{
"text_models":[model_id],
"all_text_models":[model_id],
"vision_models":[],
"image_gen_models":[],
"all_image_gen_models":[],
"search_models":[],
"all_search_models":[],
"models":[model_id],
"all_models":[model_id],
"arena_type":"text-arena"}],
"event_data": None,
"fn_index": 122,
"trigger_id": 157,
"session_hash": session_hash
}
second_payload = {
"data": [],
"event_data": None,
"fn_index": 123,
"trigger_id": 157,
"session_hash": session_hash
}
third_payload = {
"data": [None, temperature, top_p, max_tokens],
"event_data": None,
"fn_index": 124,
"trigger_id": 157,
"session_hash": session_hash
}
return first_payload, second_payload, third_payload
@classmethod
async def create_authed(
cls,
model: str,
messages: Messages,
auth_result: AuthResult,
media: MediaListType = None,
max_tokens: int = 2048,
temperature: float = 0.7,
top_p: float = 1,
conversation: JsonConversation = None,
**kwargs
) -> AsyncResult:
async def read_response(response: StreamResponse):
returned_data = ""
async for line in response.iter_lines():
if not line.startswith(b"data: "):
continue
for content in find_str(json.loads(line[6:]), 3):
if "**NETWORK ERROR DUE TO HIGH TRAFFIC." in content:
raise ResponseError(content)
if content == '<span class="cursor"></span> ' or content == 'update':
continue
if content.endswith("▌"):
content = content[:-2]
new_content = content
if content.startswith(returned_data):
new_content = content[len(returned_data):]
if not new_content:
continue
returned_data += new_content
yield new_content
if model in cls.model_aliases:
model = cls.model_aliases[model]
if isinstance(model, list):
model = random.choice(model)
prompt = get_last_user_message(messages)
async with StreamSession(**auth_result.get_dict()) as session:
if conversation is None:
conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace("-", ""))
media = list(merge_media(media, messages))
if media:
data = FormData()
for i in range(len(media)):
media[i] = (to_bytes(media[i][0]), media[i][1])
for image, image_name in media:
data.add_field(f"files", image, filename=image_name)
async with session.post(f"{cls.url}/upload", params={"upload_id": conversation.session_hash}, data=data) as response:
await raise_for_status(response)
image_files = await response.json()
media = [{
"path": image_file,
"url": f"{cls.url}/file={image_file}",
"orig_name": media[i][1],
"size": len(media[i][0]),
"mime_type": is_accepted_format(media[i][0]),
"meta": {
"_type": "gradio.FileData"
}
} for i, image_file in enumerate(image_files)]
for domain, harFile in read_har_files():
for v in harFile['log']['entries']:
request_url = v['request']['url']
if domain not in request_url or "." in urlparse(request_url).path or "heartbeat" in request_url:
continue
postData = None
if "postData" in v['request']:
postData = v['request']['postData']['text']
postData = postData.replace('"hello"', json.dumps(prompt))
postData = postData.replace('[null,0.7,1,2048]', json.dumps([None, temperature, top_p, max_tokens]))
postData = postData.replace('"files":[]', f'"files":{json.dumps(media)}')
postData = postData.replace("__SESSION__", conversation.session_hash)
if model:
postData = postData.replace("__MODEL__", model)
request_url = request_url.replace("__SESSION__", conversation.session_hash)
method = v['request']['method'].lower()
async with getattr(session, method)(request_url, data=postData) as response:
await raise_for_status(response)
async for chunk in read_response(response):
yield chunk
yield conversation
else:
first_payload, second_payload, third_payload = cls._build_second_payloads(model, conversation.session_hash, prompt, max_tokens, temperature, top_p)
# POST 1
async with session.post(f"{cls.url}{cls.api_endpoint}", json=first_payload) as response:
await raise_for_status(response)
# POST 2
async with session.post(f"{cls.url}{cls.api_endpoint}", json=second_payload) as response:
await raise_for_status(response)
# POST 3
async with session.post(f"{cls.url}{cls.api_endpoint}", json=third_payload) as response:
await raise_for_status(response)
stream_url = f"{cls.url}/queue/data?session_hash={conversation.session_hash}"
async with session.get(stream_url, headers={"Accept": "text/event-stream"}) as response:
await raise_for_status(response)
async for chunk in read_response(response):
yield chunk
def read_har_files():
for root, _, files in os.walk(os.path.dirname(__file__)):
for file in files:
if not file.endswith(".har"):
continue
with open(os.path.join(root, file), 'rb') as f:
try:
yield os.path.splitext(file)[0], json.load(f)
except json.JSONDecodeError:
raise RuntimeError(f"Failed to read HAR file: {file}")
def read_str_recusive(data):
if isinstance(data, dict):
data = data.values()
for item in data:
if isinstance(item, (list, dict)):
yield from read_str_recusive(item)
elif isinstance(item, str):
yield item
def find_str(data, skip: int = 0):
for item in read_str_recusive(data):
if skip > 0:
skip -= 1
continue
yield item
break
def read_list_recusive(data, key):
if isinstance(data, dict):
for k, v in data.items():
if k == key:
yield v
else:
yield from read_list_recusive(v, key)
elif isinstance(data, list):
for item in data:
yield from read_list_recusive(item, key)
def find_list(data, key):
for item in read_list_recusive(data, key):
if isinstance(item, str):
yield item
elif isinstance(item, list):
yield from item
def get_str_list(data):
for item in data:
if isinstance(item, list):
yield from get_str_list(item)
else:
yield item
# with open("g4f/Provider/har/lmarena.ai.har", "r") as f:
# try:
# harFile = json.loads(f.read())
# except json.JSONDecodeError:
# raise RuntimeError(f"Failed to read HAR file")
# new_entries = []
# for v in harFile['log']['entries']:
# request_url = v['request']['url']
# if not request_url.startswith("https://lmarena.ai") or "." in urlparse(request_url).path or "heartbeat" in request_url:
# continue
# v['request']['cookies'] = []
# v['request']['headers'] = [header for header in v['request']['headers'] if header['name'].lower() != "cookie"]
# v['response']['headers'] = []
# new_entries.append(v)
# print(f"Request URL: {request_url}"