Fix Bing Provider, Add Account Support for Meta AI

Use default headers in OpenaiChat
This commit is contained in:
Heiner Lohaus
2024-04-21 07:22:59 +02:00
parent db00480153
commit db2d6cffd9
14 changed files with 239 additions and 164 deletions

View File

@@ -10,7 +10,7 @@ except ImportError:
from g4f.client import Client, ChatCompletion from g4f.client import Client, ChatCompletion
from g4f.Provider import Bing, OpenaiChat, DuckDuckGo from g4f.Provider import Bing, OpenaiChat, DuckDuckGo
DEFAULT_MESSAGES = [{"role": "system", "content": 'Response in json, Example: {"success: true"}'}, DEFAULT_MESSAGES = [{"role": "system", "content": 'Response in json, Example: {"success": false}'},
{"role": "user", "content": "Say success true in json"}] {"role": "user", "content": "Say success true in json"}]
class TestProviderIntegration(unittest.TestCase): class TestProviderIntegration(unittest.TestCase):

View File

@@ -7,13 +7,13 @@ import time
import asyncio import asyncio
from urllib import parse from urllib import parse
from datetime import datetime, date from datetime import datetime, date
from aiohttp import ClientSession, ClientTimeout, BaseConnector, WSMsgType
from ..typing import AsyncResult, Messages, ImageType, Cookies from ..typing import AsyncResult, Messages, ImageType, Cookies
from ..image import ImageRequest from ..image import ImageRequest
from ..errors import ResponseStatusError, RateLimitError from ..errors import ResponseError, ResponseStatusError, RateLimitError
from ..requests import StreamSession, DEFAULT_HEADERS
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import get_connector, get_random_hex from .helper import get_random_hex
from .bing.upload_image import upload_image from .bing.upload_image import upload_image
from .bing.conversation import Conversation, create_conversation, delete_conversation from .bing.conversation import Conversation, create_conversation, delete_conversation
from .BingCreateImages import BingCreateImages from .BingCreateImages import BingCreateImages
@@ -49,7 +49,6 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
timeout: int = 900, timeout: int = 900,
api_key: str = None, api_key: str = None,
cookies: Cookies = None, cookies: Cookies = None,
connector: BaseConnector = None,
tone: str = None, tone: str = None,
image: ImageType = None, image: ImageType = None,
web_search: bool = False, web_search: bool = False,
@@ -79,7 +78,6 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
return stream_generate( return stream_generate(
prompt, tone, image, context, cookies, api_key, prompt, tone, image, context, cookies, api_key,
get_connector(connector, proxy, True),
proxy, web_search, gpt4_turbo, timeout, proxy, web_search, gpt4_turbo, timeout,
**kwargs **kwargs
) )
@@ -102,25 +100,53 @@ def get_ip_address() -> str:
return f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}" return f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}"
def get_default_cookies(): def get_default_cookies():
#muid = get_random_hex().upper()
sid = get_random_hex().upper()
guid = get_random_hex().upper()
isodate = date.today().isoformat()
timestamp = int(time.time())
zdate = "0001-01-01T00:00:00.0000000"
return { return {
'SRCHD' : 'AF=NOFORM', "_C_Auth": "",
'PPLState' : '1', #"MUID": muid,
'KievRPSSecAuth': '', #"MUIDB": muid,
'SUID' : '', "_EDGE_S": f"F=1&SID={sid}",
'SRCHUSR' : f'DOB={date.today().strftime("%Y%m%d")}&T={int(time.time())}', "_EDGE_V": "1",
'SRCHHPGUSR' : f'HV={int(time.time())}', "SRCHD": "AF=hpcodx",
'BCP' : 'AD=1&AL=1&SM=1', "SRCHUID": f"V=2&GUID={guid}&dmnchg=1",
'_Rwho' : f'u=d&ts={date.today().isoformat()}', "_RwBf": (
f"r=0&ilt=1&ihpd=0&ispd=0&rc=3&rb=0&gb=0&rg=200&pc=0&mtu=0&rbb=0&g=0&cid="
f"&clo=0&v=1&l={isodate}&lft={zdate}&aof=0&ard={zdate}"
f"&rwdbt={zdate}&rwflt={zdate}&o=2&p=&c=&t=0&s={zdate}"
f"&ts={isodate}&rwred=0&wls=&wlb="
"&wle=&ccp=&cpt=&lka=0&lkt=0&aad=0&TH="
),
'_Rwho': f'u=d&ts={isodate}',
"_SS": f"SID={sid}&R=3&RB=0&GB=0&RG=200&RP=0",
"SRCHUSR": f"DOB={date.today().strftime('%Y%m%d')}&T={timestamp}",
"SRCHHPGUSR": f"HV={int(time.time())}",
"BCP": "AD=1&AL=1&SM=1",
"ipv6": f"hit={timestamp}",
'_C_ETH' : '1',
} }
def create_headers(cookies: Cookies = None, api_key: str = None) -> dict: async def create_headers(cookies: Cookies = None, api_key: str = None) -> dict:
if cookies is None: if cookies is None:
# import nodriver as uc
# browser = await uc.start(headless=False)
# page = await browser.get(Defaults.home)
# await asyncio.sleep(10)
# cookies = {}
# for c in await page.browser.cookies.get_all():
# if c.domain.endswith(".bing.com"):
# cookies[c.name] = c.value
# user_agent = await page.evaluate("window.navigator.userAgent")
# await page.close()
cookies = get_default_cookies() cookies = get_default_cookies()
if api_key is not None: if api_key is not None:
cookies["_U"] = api_key cookies["_U"] = api_key
headers = Defaults.headers.copy() headers = Defaults.headers.copy()
headers["cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items()) headers["cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
headers["x-forwarded-for"] = get_ip_address()
return headers return headers
class Defaults: class Defaults:
@@ -246,25 +272,13 @@ class Defaults:
} }
# Default headers for requests # Default headers for requests
home = 'https://www.bing.com/chat?q=Bing+AI&FORM=hpcodx' home = "https://www.bing.com/chat?q=Microsoft+Copilot&FORM=hpcodx"
headers = { headers = {
'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"', **DEFAULT_HEADERS,
'sec-ch-ua-mobile': '?0', "accept": "application/json",
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', "referer": home,
'sec-ch-ua-arch': '"x86"',
'sec-ch-ua-full-version': '"122.0.6261.69"',
'accept': 'application/json',
'sec-ch-ua-platform-version': '"15.0.0"',
"x-ms-client-request-id": str(uuid.uuid4()), "x-ms-client-request-id": str(uuid.uuid4()),
'sec-ch-ua-full-version-list': '"Chromium";v="122.0.6261.69", "Not(A:Brand";v="24.0.0.0", "Google Chrome";v="122.0.6261.69"', "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.15.1 OS/Windows",
'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.12.3 OS/Windows',
'sec-ch-ua-model': '""',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': home,
'accept-language': 'en-US,en;q=0.9',
} }
def format_message(msg: dict) -> str: def format_message(msg: dict) -> str:
@@ -368,7 +382,6 @@ async def stream_generate(
context: str = None, context: str = None,
cookies: dict = None, cookies: dict = None,
api_key: str = None, api_key: str = None,
connector: BaseConnector = None,
proxy: str = None, proxy: str = None,
web_search: bool = False, web_search: bool = False,
gpt4_turbo: bool = False, gpt4_turbo: bool = False,
@@ -393,14 +406,12 @@ async def stream_generate(
:param timeout: Timeout for the request. :param timeout: Timeout for the request.
:return: An asynchronous generator yielding responses. :return: An asynchronous generator yielding responses.
""" """
headers = create_headers(cookies, api_key) headers = await create_headers(cookies, api_key)
new_conversation = conversation is None new_conversation = conversation is None
max_retries = (5 if new_conversation else 0) if max_retries is None else max_retries max_retries = (5 if new_conversation else 0) if max_retries is None else max_retries
async with ClientSession( first = True
timeout=ClientTimeout(total=timeout), connector=connector while first or conversation is None:
) as session: async with StreamSession(timeout=timeout, proxy=proxy) as session:
first = True
while first or conversation is None:
first = False first = False
do_read = True do_read = True
try: try:
@@ -408,13 +419,13 @@ async def stream_generate(
conversation = await create_conversation(session, headers, tone) conversation = await create_conversation(session, headers, tone)
if return_conversation: if return_conversation:
yield conversation yield conversation
except ResponseStatusError as e: except (ResponseStatusError, RateLimitError) as e:
max_retries -= 1 max_retries -= 1
if max_retries < 1: if max_retries < 1:
raise e raise e
if debug.logging: if debug.logging:
print(f"Bing: Retry: {e}") print(f"Bing: Retry: {e}")
headers = create_headers() headers = await create_headers()
await asyncio.sleep(sleep_retry) await asyncio.sleep(sleep_retry)
continue continue
@@ -434,7 +445,7 @@ async def stream_generate(
) as wss: ) as wss:
await wss.send_str(format_message({'protocol': 'json', 'version': 1})) await wss.send_str(format_message({'protocol': 'json', 'version': 1}))
await wss.send_str(format_message({"type": 6})) await wss.send_str(format_message({"type": 6}))
await wss.receive(timeout=timeout) await wss.receive_str()
await wss.send_str(create_message( await wss.send_str(create_message(
conversation, prompt, tone, conversation, prompt, tone,
context if new_conversation else None, context if new_conversation else None,
@@ -445,16 +456,15 @@ async def stream_generate(
returned_text = '' returned_text = ''
message_id = None message_id = None
while do_read: while do_read:
msg = await wss.receive(timeout=timeout) msg = await wss.receive_str()
if msg.type == WSMsgType.CLOSED: objects = msg.split(Defaults.delimiter)
break
if msg.type != WSMsgType.TEXT or not msg.data:
continue
objects = msg.data.split(Defaults.delimiter)
for obj in objects: for obj in objects:
if obj is None or not obj: if obj is None or not obj:
continue continue
response = json.loads(obj) try:
response = json.loads(obj)
except json.JSONDecodeError:
continue
if response and response.get('type') == 1 and response['arguments'][0].get('messages'): if response and response.get('type') == 1 and response['arguments'][0].get('messages'):
message = response['arguments'][0]['messages'][0] message = response['arguments'][0]['messages'][0]
if message_id is not None and message_id != message["messageId"]: if message_id is not None and message_id != message["messageId"]:
@@ -462,7 +472,7 @@ async def stream_generate(
message_id = message["messageId"] message_id = message["messageId"]
image_response = None image_response = None
if (raise_apology and message['contentOrigin'] == 'Apology'): if (raise_apology and message['contentOrigin'] == 'Apology'):
raise RuntimeError("Apology Response Error") raise ResponseError("Apology Response Error")
if 'adaptiveCards' in message: if 'adaptiveCards' in message:
card = message['adaptiveCards'][0]['body'][0] card = message['adaptiveCards'][0]['body'][0]
if "text" in card: if "text" in card:
@@ -488,6 +498,7 @@ async def stream_generate(
yield image_response yield image_response
elif response.get('type') == 2: elif response.get('type') == 2:
result = response['item']['result'] result = response['item']['result']
do_read = False
if result.get('error'): if result.get('error'):
max_retries -= 1 max_retries -= 1
if max_retries < 1: if max_retries < 1:
@@ -497,10 +508,12 @@ async def stream_generate(
raise RuntimeError(f"{result['value']}: {result['message']}") raise RuntimeError(f"{result['value']}: {result['message']}")
if debug.logging: if debug.logging:
print(f"Bing: Retry: {result['value']}: {result['message']}") print(f"Bing: Retry: {result['value']}: {result['message']}")
headers = create_headers() headers = await create_headers()
do_read = False
conversation = None conversation = None
await asyncio.sleep(sleep_retry) await asyncio.sleep(sleep_retry)
break break
return elif response.get('type') == 3:
await delete_conversation(session, conversation, headers) do_read = False
break
if conversation is not None:
await delete_conversation(session, conversation, headers)

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import json import json
import uuid import uuid
import random import random
@@ -8,6 +10,8 @@ from aiohttp import ClientSession, BaseConnector
from ..typing import AsyncResult, Messages, Cookies from ..typing import AsyncResult, Messages, Cookies
from ..requests import raise_for_status, DEFAULT_HEADERS from ..requests import raise_for_status, DEFAULT_HEADERS
from ..image import ImageResponse, ImagePreview
from ..errors import ResponseError
from .base_provider import AsyncGeneratorProvider from .base_provider import AsyncGeneratorProvider
from .helper import format_prompt, get_connector from .helper import format_prompt, get_connector
@@ -22,6 +26,7 @@ class AbraGeoBlockedError(Exception):
pass pass
class MetaAI(AsyncGeneratorProvider): class MetaAI(AsyncGeneratorProvider):
label = "Meta AI"
url = "https://www.meta.ai" url = "https://www.meta.ai"
working = True working = True
@@ -38,11 +43,10 @@ class MetaAI(AsyncGeneratorProvider):
proxy: str = None, proxy: str = None,
**kwargs **kwargs
) -> AsyncResult: ) -> AsyncResult:
#cookies = get_cookies(".meta.ai", False, True)
async for chunk in cls(proxy).prompt(format_prompt(messages)): async for chunk in cls(proxy).prompt(format_prompt(messages)):
yield chunk yield chunk
async def get_access_token(self, birthday: str = "1999-01-01") -> str: async def update_access_token(self, birthday: str = "1999-01-01"):
url = "https://www.meta.ai/api/graphql/" url = "https://www.meta.ai/api/graphql/"
payload = { payload = {
@@ -66,25 +70,37 @@ class MetaAI(AsyncGeneratorProvider):
async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response: async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
await raise_for_status(response, "Fetch access_token failed") await raise_for_status(response, "Fetch access_token failed")
auth_json = await response.json(content_type=None) auth_json = await response.json(content_type=None)
access_token = auth_json["data"]["xab_abra_accept_terms_of_service"]["new_temp_user_auth"]["access_token"] self.access_token = auth_json["data"]["xab_abra_accept_terms_of_service"]["new_temp_user_auth"]["access_token"]
return access_token
async def prompt(self, message: str, cookies: Cookies = None) -> AsyncResult: async def prompt(self, message: str, cookies: Cookies = None) -> AsyncResult:
if cookies is not None:
self.cookies = cookies
self.access_token = None
if self.cookies is None: if self.cookies is None:
self.cookies = await self.get_cookies() await self.update_cookies(cookies)
if self.access_token is None: if cookies is not None:
self.access_token = await self.get_access_token() self.access_token = None
if self.access_token is None and cookies is None:
await self.update_access_token()
url = "https://graph.meta.ai/graphql?locale=user" if self.access_token is None:
#url = "https://www.meta.ai/api/graphql/" url = "https://www.meta.ai/api/graphql/"
payload = {"lsd": self.lsd, 'fb_dtsg': self.dtsg}
headers = {'x-fb-lsd': self.lsd}
else:
url = "https://graph.meta.ai/graphql?locale=user"
payload = {"access_token": self.access_token}
headers = {}
headers = {
'content-type': 'application/x-www-form-urlencoded',
'cookie': "; ".join([f"{k}={v}" for k, v in cookies.items()]),
'origin': 'https://www.meta.ai',
'referer': 'https://www.meta.ai/',
'x-asbd-id': '129477',
'x-fb-friendly-name': 'useAbraSendMessageMutation',
**headers
}
payload = { payload = {
"access_token": self.access_token, **payload,
#"lsd": cookies["lsd"], 'fb_api_caller_class': 'RelayModern',
"fb_api_caller_class": "RelayModern", 'fb_api_req_friendly_name': 'useAbraSendMessageMutation',
"fb_api_req_friendly_name": "useAbraSendMessageMutation",
"variables": json.dumps({ "variables": json.dumps({
"message": {"sensitive_string_value": message}, "message": {"sensitive_string_value": message},
"externalConversationId": str(uuid.uuid4()), "externalConversationId": str(uuid.uuid4()),
@@ -98,19 +114,16 @@ class MetaAI(AsyncGeneratorProvider):
"__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False, "__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
"__relay_internal__pv__WebPixelRatiorelayprovider": 1, "__relay_internal__pv__WebPixelRatiorelayprovider": 1,
}), }),
"server_timestamps": "true", 'server_timestamps': 'true',
"doc_id": "7783822248314888", 'doc_id': '7783822248314888'
} }
headers = { async with self.session.post(url, headers=headers, data=payload) as response:
"x-asbd-id": "129477",
"x-fb-friendly-name": "useAbraSendMessageMutation",
#"x-fb-lsd": cookies["lsd"],
}
async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
await raise_for_status(response, "Fetch response failed") await raise_for_status(response, "Fetch response failed")
last_snippet_len = 0 last_snippet_len = 0
fetch_id = None fetch_id = None
async for line in response.content: async for line in response.content:
if b"<h1>Something Went Wrong</h1>" in line:
raise ResponseError("Response: Something Went Wrong")
try: try:
json_line = json.loads(line) json_line = json.loads(line)
except json.JSONDecodeError: except json.JSONDecodeError:
@@ -119,7 +132,14 @@ class MetaAI(AsyncGeneratorProvider):
streaming_state = bot_response_message.get("streaming_state") streaming_state = bot_response_message.get("streaming_state")
fetch_id = bot_response_message.get("fetch_id") or fetch_id fetch_id = bot_response_message.get("fetch_id") or fetch_id
if streaming_state in ("STREAMING", "OVERALL_DONE"): if streaming_state in ("STREAMING", "OVERALL_DONE"):
#imagine_card = bot_response_message["imagine_card"] imagine_card = bot_response_message.get("imagine_card")
if imagine_card is not None:
imagine_session = imagine_card.get("session")
if imagine_session is not None:
imagine_medias = imagine_session.get("media_sets", {}).pop().get("imagine_media")
if imagine_medias is not None:
image_class = ImageResponse if streaming_state == "OVERALL_DONE" else ImagePreview
yield image_class([media["uri"] for media in imagine_medias], imagine_medias[0]["prompt"])
snippet = bot_response_message["snippet"] snippet = bot_response_message["snippet"]
new_snippet_len = len(snippet) new_snippet_len = len(snippet)
if new_snippet_len > last_snippet_len: if new_snippet_len > last_snippet_len:
@@ -135,7 +155,7 @@ class MetaAI(AsyncGeneratorProvider):
if sources is not None: if sources is not None:
yield sources yield sources
async def get_cookies(self, cookies: Cookies = None) -> Cookies: async def update_cookies(self, cookies: Cookies = None):
async with self.session.get("https://www.meta.ai/", cookies=cookies) as response: async with self.session.get("https://www.meta.ai/", cookies=cookies) as response:
await raise_for_status(response, "Fetch home failed") await raise_for_status(response, "Fetch home failed")
text = await response.text() text = await response.text()
@@ -148,12 +168,20 @@ class MetaAI(AsyncGeneratorProvider):
"datr": self.extract_value(text, "datr"), "datr": self.extract_value(text, "datr"),
} }
self.lsd = self.extract_value(text, start_str='"LSD",[],{"token":"', end_str='"}') self.lsd = self.extract_value(text, start_str='"LSD",[],{"token":"', end_str='"}')
return cookies self.dtsg = self.extract_value(text, start_str='"DTSGInitialData",[],{"token":"', end_str='"}')
self.cookies = cookies
async def fetch_sources(self, fetch_id: str) -> Sources: async def fetch_sources(self, fetch_id: str) -> Sources:
url = "https://graph.meta.ai/graphql?locale=user" if self.access_token is None:
url = "https://www.meta.ai/api/graphql/"
payload = {"lsd": self.lsd, 'fb_dtsg': self.dtsg}
headers = {'x-fb-lsd': self.lsd}
else:
url = "https://graph.meta.ai/graphql?locale=user"
payload = {"access_token": self.access_token}
headers = {}
payload = { payload = {
"access_token": self.access_token, **payload,
"fb_api_caller_class": "RelayModern", "fb_api_caller_class": "RelayModern",
"fb_api_req_friendly_name": "AbraSearchPluginDialogQuery", "fb_api_req_friendly_name": "AbraSearchPluginDialogQuery",
"variables": json.dumps({"abraMessageFetchID": fetch_id}), "variables": json.dumps({"abraMessageFetchID": fetch_id}),
@@ -163,18 +191,22 @@ class MetaAI(AsyncGeneratorProvider):
headers = { headers = {
"authority": "graph.meta.ai", "authority": "graph.meta.ai",
"x-fb-friendly-name": "AbraSearchPluginDialogQuery", "x-fb-friendly-name": "AbraSearchPluginDialogQuery",
**headers
} }
async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response: async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
await raise_for_status(response) await raise_for_status(response)
response_json = await response.json() text = await response.text()
if "<h1>Something Went Wrong</h1>" in text:
raise ResponseError("Response: Something Went Wrong")
try: try:
response_json = json.loads(text)
message = response_json["data"]["message"] message = response_json["data"]["message"]
if message is not None: if message is not None:
searchResults = message["searchResults"] searchResults = message["searchResults"]
if searchResults is not None: if searchResults is not None:
return Sources(searchResults["references"]) return Sources(searchResults["references"])
except (KeyError, TypeError): except (KeyError, TypeError, json.JSONDecodeError):
raise RuntimeError(f"Response: {response_json}") raise RuntimeError(f"Response: {text}")
@staticmethod @staticmethod
def extract_value(text: str, key: str = None, start_str = None, end_str = '",') -> str: def extract_value(text: str, key: str = None, start_str = None, end_str = '",') -> str:

View File

@@ -0,0 +1,21 @@
from __future__ import annotations
from ..typing import AsyncResult, Messages, Cookies
from .helper import format_prompt, get_cookies
from .MetaAI import MetaAI
class MetaAIAccount(MetaAI):
needs_auth = True
@classmethod
async def create_async_generator(
cls,
model: str,
messages: Messages,
proxy: str = None,
cookies: Cookies = None,
**kwargs
) -> AsyncResult:
cookies = get_cookies(".meta.ai", True, True) if cookies is None else cookies
async for chunk in cls(proxy).prompt(format_prompt(messages), cookies):
yield chunk

View File

@@ -10,7 +10,7 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt from .helper import format_prompt
from ..image import ImageResponse, to_bytes, is_accepted_format from ..image import ImageResponse, to_bytes, is_accepted_format
from ..requests import StreamSession, FormData, raise_for_status from ..requests import StreamSession, FormData, raise_for_status
from .you.har_file import get_dfp_telemetry_id from .you.har_file import get_telemetry_ids
class You(AsyncGeneratorProvider, ProviderModelMixin): class You(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://you.com" url = "https://you.com"
@@ -35,6 +35,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
} }
_cookies = None _cookies = None
_cookies_used = 0 _cookies_used = 0
_telemetry_ids = []
@classmethod @classmethod
async def create_async_generator( async def create_async_generator(
@@ -159,6 +160,8 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
@classmethod @classmethod
async def create_cookies(cls, client: StreamSession) -> Cookies: async def create_cookies(cls, client: StreamSession) -> Cookies:
if not cls._telemetry_ids:
cls._telemetry_ids = await get_telemetry_ids()
user_uuid = str(uuid.uuid4()) user_uuid = str(uuid.uuid4())
async with client.post( async with client.post(
"https://web.stytch.com/sdk/v1/passwords", "https://web.stytch.com/sdk/v1/passwords",
@@ -170,7 +173,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
"Referer": "https://you.com/" "Referer": "https://you.com/"
}, },
json={ json={
"dfp_telemetry_id": await get_dfp_telemetry_id(), "dfp_telemetry_id": cls._telemetry_ids.pop(),
"email": f"{user_uuid}@gmail.com", "email": f"{user_uuid}@gmail.com",
"password": f"{user_uuid}#{user_uuid}", "password": f"{user_uuid}#{user_uuid}",
"session_duration_minutes": 129600 "session_duration_minutes": 129600

View File

@@ -43,6 +43,7 @@ from .Liaobots import Liaobots
from .Llama import Llama from .Llama import Llama
from .Local import Local from .Local import Local
from .MetaAI import MetaAI from .MetaAI import MetaAI
from .MetaAIAccount import MetaAIAccount
from .PerplexityLabs import PerplexityLabs from .PerplexityLabs import PerplexityLabs
from .Pi import Pi from .Pi import Pi
from .ReplicateImage import ReplicateImage from .ReplicateImage import ReplicateImage

View File

@@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
from aiohttp import ClientSession from ...requests import StreamSession, raise_for_status
from ...requests import raise_for_status
from ...errors import RateLimitError from ...errors import RateLimitError
from ...providers.conversation import BaseConversation from ...providers.conversation import BaseConversation
@@ -22,7 +21,7 @@ class Conversation(BaseConversation):
self.clientId = clientId self.clientId = clientId
self.conversationSignature = conversationSignature self.conversationSignature = conversationSignature
async def create_conversation(session: ClientSession, headers: dict, tone: str) -> Conversation: async def create_conversation(session: StreamSession, headers: dict, tone: str) -> Conversation:
""" """
Create a new conversation asynchronously. Create a new conversation asynchronously.
@@ -49,7 +48,7 @@ async def create_conversation(session: ClientSession, headers: dict, tone: str)
raise RuntimeError('Empty fields: Failed to create conversation') raise RuntimeError('Empty fields: Failed to create conversation')
return Conversation(conversationId, clientId, conversationSignature) return Conversation(conversationId, clientId, conversationSignature)
async def list_conversations(session: ClientSession) -> list: async def list_conversations(session: StreamSession) -> list:
""" """
List all conversations asynchronously. List all conversations asynchronously.
@@ -64,7 +63,7 @@ async def list_conversations(session: ClientSession) -> list:
response = await response.json() response = await response.json()
return response["chats"] return response["chats"]
async def delete_conversation(session: ClientSession, conversation: Conversation, headers: dict) -> bool: async def delete_conversation(session: StreamSession, conversation: Conversation, headers: dict) -> bool:
""" """
Delete a conversation asynchronously. Delete a conversation asynchronously.

View File

@@ -24,7 +24,7 @@ except ImportError:
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ...webdriver import get_browser from ...webdriver import get_browser
from ...typing import AsyncResult, Messages, Cookies, ImageType, AsyncIterator from ...typing import AsyncResult, Messages, Cookies, ImageType, AsyncIterator
from ...requests import get_args_from_browser, raise_for_status from ...requests import DEFAULT_HEADERS, get_args_from_browser, raise_for_status
from ...requests.aiohttp import StreamSession from ...requests.aiohttp import StreamSession
from ...image import to_image, to_bytes, ImageResponse, ImageRequest from ...image import to_image, to_bytes, ImageResponse, ImageRequest
from ...errors import MissingAuthError, ResponseError from ...errors import MissingAuthError, ResponseError
@@ -360,7 +360,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
if debug.logging: if debug.logging:
print("OpenaiChat: Load default_model failed") print("OpenaiChat: Load default_model failed")
print(f"{e.__class__.__name__}: {e}") print(f"{e.__class__.__name__}: {e}")
arkose_token = None arkose_token = None
if cls.default_model is None: if cls.default_model is None:
@@ -377,7 +376,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers)) cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
async with session.post( async with session.post(
f"{cls.url}/backend-anon/sentinel/chat-requirements" if not cls._api_key else f"{cls.url}/backend-anon/sentinel/chat-requirements"
if not cls._api_key else
f"{cls.url}/backend-api/sentinel/chat-requirements", f"{cls.url}/backend-api/sentinel/chat-requirements",
json={"conversation_mode_kind": "primary_assistant"}, json={"conversation_mode_kind": "primary_assistant"},
headers=cls._headers headers=cls._headers
@@ -388,7 +388,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
blob = data["arkose"]["dx"] blob = data["arkose"]["dx"]
need_arkose = data["arkose"]["required"] need_arkose = data["arkose"]["required"]
chat_token = data["token"] chat_token = data["token"]
if debug.logging: if debug.logging:
print(f'Arkose: {need_arkose} Turnstile: {data["turnstile"]["required"]}') print(f'Arkose: {need_arkose} Turnstile: {data["turnstile"]["required"]}')
@@ -595,8 +595,7 @@ this.fetch = async (url, options) => {
print(f"Open nodriver with user_dir: {user_data_dir}") print(f"Open nodriver with user_dir: {user_data_dir}")
browser = await uc.start(user_data_dir=user_data_dir) browser = await uc.start(user_data_dir=user_data_dir)
page = await browser.get("https://chat.openai.com/") page = await browser.get("https://chat.openai.com/")
while await page.find("[id^=headlessui-menu-button-]") is None: await page.select("[id^=headlessui-menu-button-]", 240)
await asyncio.sleep(1)
api_key = await page.evaluate( api_key = await page.evaluate(
"(async () => {" "(async () => {"
"let session = await fetch('/api/auth/session');" "let session = await fetch('/api/auth/session');"
@@ -662,16 +661,10 @@ this.fetch = async (url, options) => {
@staticmethod @staticmethod
def get_default_headers() -> dict: def get_default_headers() -> dict:
return { return {
"accept-language": "en-US", **DEFAULT_HEADERS,
"content-type": "application/json", "content-type": "application/json",
"oai-device-id": str(uuid.uuid4()), "oai-device-id": str(uuid.uuid4()),
"oai-language": "en-US", "oai-language": "en-US",
"sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Linux\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
} }
@staticmethod @staticmethod
@@ -696,11 +689,11 @@ this.fetch = async (url, options) => {
def _set_api_key(cls, api_key: str): def _set_api_key(cls, api_key: str):
cls._api_key = api_key cls._api_key = api_key
cls._expires = int(time.time()) + 60 * 60 * 4 cls._expires = int(time.time()) + 60 * 60 * 4
cls._headers["Authorization"] = f"Bearer {api_key}" cls._headers["authorization"] = f"Bearer {api_key}"
@classmethod @classmethod
def _update_cookie_header(cls): def _update_cookie_header(cls):
cls._headers["Cookie"] = cls._format_cookies(cls._cookies) cls._headers["cookie"] = cls._format_cookies(cls._cookies)
class Conversation(BaseConversation): class Conversation(BaseConversation):
""" """

View File

@@ -3,11 +3,10 @@ from __future__ import annotations
import json import json
import os import os
import random import random
import uuid
import asyncio
import requests import requests
from ...requests import StreamSession, raise_for_status from ...requests import StreamSession, raise_for_status
from ... import debug
class NoValidHarFileError(Exception): class NoValidHarFileError(Exception):
... ...
@@ -67,60 +66,49 @@ async def sendRequest(tmpArk: arkReq, proxy: str = None):
return await response.text() return await response.text()
async def get_dfp_telemetry_id(proxy: str = None): async def get_dfp_telemetry_id(proxy: str = None):
return await telemetry_id_with_driver(proxy)
global chatArks global chatArks
if chatArks is None: if chatArks is None:
chatArks = readHAR() chatArks = readHAR()
return await sendRequest(random.choice(chatArks), proxy) return await sendRequest(random.choice(chatArks), proxy)
async def telemetry_id_with_driver(proxy: str = None): def read_telemetry_file() -> list:
from ...debug import logging with open("hardir/you.com_telemetry_ids.txt", "r") as f:
if logging: ids = f.readlines()
print('getting telemetry_id for you.com with nodriver') random.shuffle(ids)
return ids
async def get_telemetry_ids(proxy: str = None) -> list:
if debug.logging:
print('Getting telemetry_id for you.com with nodriver')
try: try:
import nodriver as uc from nodriver import start
from nodriver import start, cdp, loop
except ImportError: except ImportError:
if logging: if debug.logging:
print('nodriver not found, random uuid (may fail)') print('Install "nodriver" package | pip install -U nodriver')
return str(uuid.uuid4()) return read_telemetry_file()
CAN_EVAL = False
payload_received = False
payload = None
try: try:
browser = await start() browser = await start()
tab = browser.main_tab tab = browser.main_tab
async def send_handler(event: cdp.network.RequestWillBeSent):
nonlocal CAN_EVAL, payload_received, payload
if 'telemetry.js' in event.request.url:
CAN_EVAL = True
if "/submit" in event.request.url:
payload = event.request.post_data
payload_received = True
tab.add_handler(cdp.network.RequestWillBeSent, send_handler)
await browser.get("https://you.com") await browser.get("https://you.com")
while not CAN_EVAL: while not await tab.evaluate('"GetTelemetryID" in this'):
await tab.sleep(1) await tab.sleep(1)
await tab.evaluate('window.GetTelemetryID("public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819", "https://telemetry.stytch.com/submit");') async def get_telemetry_id():
public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
telemetry_url = "https://telemetry.stytch.com/submit"
return await tab.evaluate(f'this.GetTelemetryID("{public_token}", "{telemetry_url}");', await_promise=True)
while not payload_received: # for _ in range(500):
await tab.sleep(.1) # with open("hardir/you.com_telemetry_ids.txt", "a") as f:
# f.write((await get_telemetry_id()) + "\n")
except Exception as e:
print(f"Error occurred: {str(e)}")
return [await get_telemetry_id() for _ in range(10)]
finally: finally:
try: try:
await tab.close() await tab.close()
except Exception as e: except Exception as e:
print(f"Error occurred while closing tab: {str(e)}") print(f"Error occurred while closing tab: {str(e)}")
try: try:
await browser.stop() await browser.stop()
except Exception as e: except Exception as e:

View File

@@ -41,7 +41,9 @@ appStorage = window.localStorage || {
length: 0 length: 0
} }
const markdown = window.markdownit(); const markdown = window.markdownit({
html: true,
});
const markdown_render = (content) => { const markdown_render = (content) => {
return markdown.render(content return markdown.render(content
.replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "") .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
@@ -312,6 +314,8 @@ async function add_message_chunk(message) {
window.error = message.error window.error = message.error
console.error(message.error); console.error(message.error);
content_inner.innerHTML += `<p><strong>An error occured:</strong> ${message.error}</p>`; content_inner.innerHTML += `<p><strong>An error occured:</strong> ${message.error}</p>`;
} else if (message.type == "preview") {
content_inner.innerHTML = markdown_render(message.preview);
} else if (message.type == "content") { } else if (message.type == "content") {
window.text += message.content; window.text += message.content;
html = markdown_render(window.text); html = markdown_render(window.text);
@@ -545,7 +549,7 @@ const load_conversation = async (conversation_id, scroll=true) => {
last_model = item.provider?.model; last_model = item.provider?.model;
let next_i = parseInt(i) + 1; let next_i = parseInt(i) + 1;
let next_provider = item.provider ? item.provider : (messages.length > next_i ? messages[next_i].provider : null); let next_provider = item.provider ? item.provider : (messages.length > next_i ? messages[next_i].provider : null);
let provider_label = item.provider?.label ? item.provider?.label : item.provider?.name; let provider_label = item.provider?.label ? item.provider.label : item.provider?.name;
let provider_link = item.provider?.name ? `<a href="${item.provider.url}" target="_blank">${provider_label}</a>` : ""; let provider_link = item.provider?.name ? `<a href="${item.provider.url}" target="_blank">${provider_label}</a>` : "";
let provider = provider_link ? ` let provider = provider_link ? `
<div class="provider"> <div class="provider">

View File

@@ -7,6 +7,7 @@ from typing import Iterator
from g4f import version, models from g4f import version, models
from g4f import get_last_provider, ChatCompletion from g4f import get_last_provider, ChatCompletion
from g4f.errors import VersionNotFoundError from g4f.errors import VersionNotFoundError
from g4f.image import ImagePreview
from g4f.Provider import ProviderType, __providers__, __map__ from g4f.Provider import ProviderType, __providers__, __map__
from g4f.providers.base_provider import ProviderModelMixin, FinishReason from g4f.providers.base_provider import ProviderModelMixin, FinishReason
from g4f.providers.conversation import BaseConversation from g4f.providers.conversation import BaseConversation
@@ -146,6 +147,8 @@ class Api():
elif isinstance(chunk, Exception): elif isinstance(chunk, Exception):
logging.exception(chunk) logging.exception(chunk)
yield self._format_json("message", get_error_message(chunk)) yield self._format_json("message", get_error_message(chunk))
elif isinstance(chunk, ImagePreview):
yield self._format_json("preview", chunk.to_string())
elif not isinstance(chunk, FinishReason): elif not isinstance(chunk, FinishReason):
yield self._format_json("content", str(chunk)) yield self._format_json("content", str(chunk))
except Exception as e: except Exception as e:

View File

@@ -210,7 +210,9 @@ def format_images_markdown(images: Union[str, list], alt: str, preview: Union[st
if not isinstance(preview, list): if not isinstance(preview, list):
preview = [preview.replace('{image}', image) if preview else image for image in images] preview = [preview.replace('{image}', image) if preview else image for image in images]
result = "\n".join( result = "\n".join(
f"[![#{idx+1} {alt}]({preview[idx]})]({image})" for idx, image in enumerate(images) #f"[![#{idx+1} {alt}]({preview[idx]})]({image})"
f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
for idx, image in enumerate(images)
) )
start_flag = "<!-- generated images start -->\n" start_flag = "<!-- generated images start -->\n"
end_flag = "<!-- generated images end -->\n" end_flag = "<!-- generated images end -->\n"
@@ -259,6 +261,13 @@ class ImageResponse:
def get_list(self) -> list[str]: def get_list(self) -> list[str]:
return [self.images] if isinstance(self.images, str) else self.images return [self.images] if isinstance(self.images, str) else self.images
class ImagePreview(ImageResponse):
def __str__(self):
return ""
def to_string(self):
return super().__str__()
class ImageRequest: class ImageRequest:
def __init__( def __init__(
self, self,

View File

@@ -24,6 +24,7 @@ class StreamSession(ClientSession):
headers: dict = {}, headers: dict = {},
timeout: int = None, timeout: int = None,
connector: BaseConnector = None, connector: BaseConnector = None,
proxy: str = None,
proxies: dict = {}, proxies: dict = {},
impersonate = None, impersonate = None,
**kwargs **kwargs
@@ -38,11 +39,13 @@ class StreamSession(ClientSession):
connect, timeout = timeout; connect, timeout = timeout;
if timeout is not None: if timeout is not None:
timeout = ClientTimeout(timeout, connect) timeout = ClientTimeout(timeout, connect)
if proxy is None:
proxy = proxies.get("all", proxies.get("https"))
super().__init__( super().__init__(
**kwargs, **kwargs,
timeout=timeout, timeout=timeout,
response_class=StreamResponse, response_class=StreamResponse,
connector=get_connector(connector, proxies.get("all", proxies.get("https"))), connector=get_connector(connector, proxy),
headers=headers headers=headers
) )

View File

@@ -1,21 +1,27 @@
try:
import brotli
has_brotli = True
except ImportError:
has_brotli = False
DEFAULT_HEADERS = { DEFAULT_HEADERS = {
"sec-ch-ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
"sec-ch-ua-mobile": "?0",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"ec-ch-ua-arch": '"x86"',
"sec-ch-ua-full-version": '"122.0.6261.69"',
"accept": "*/*", "accept": "*/*",
"sec-ch-ua-platform-version:": '"6.5.0"', "accept-encoding": "gzip, deflate" + (", br" if has_brotli else ""),
"sec-ch-ua-full-version-list": '"Chromium";v="122.0.6261.69", "Not(A:Brand";v="24.0.0.0", "Google Chrome";v="122.0.6261.69"',
"sec-ch-ua-bitness": '"64"',
"sec-ch-ua-model": '""',
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-site": "same-site",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
"referer": "",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US", "accept-language": "en-US",
"referer": "",
"sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
"sec-ch-ua-arch": "\"x86\"",
"sec-ch-ua-bitness": "\"64\"",
"sec-ch-ua-full-version": "\"123.0.6312.122\"",
"sec-ch-ua-full-version-list": "\"Google Chrome\";v=\"123.0.6312.122\", \"Not:A-Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"123.0.6312.122\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-model": "\"\"",
"sec-ch-ua-platform": "\"Windows\"",
"sec-ch-ua-platform-version": '"15.0.0"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
} }
WEBVIEW_HAEDERS = { WEBVIEW_HAEDERS = {
"Accept": "*/*", "Accept": "*/*",