From 932d3a3ca092756cc3025e427fabf9ab674350fc Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Sat, 20 Apr 2024 10:43:53 +0200 Subject: Add MetaAI Provider and some small improvments --- g4f/Provider/BingCreateImages.py | 1 + g4f/Provider/Ecosia.py | 8 +- g4f/Provider/MetaAI.py | 199 +++++++++++++++++++++++++++++++ g4f/Provider/__init__.py | 1 + g4f/Provider/base_provider.py | 2 +- g4f/Provider/needs_auth/Groq.py | 2 +- g4f/Provider/needs_auth/OpenaiAccount.py | 1 - 7 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 g4f/Provider/MetaAI.py (limited to 'g4f/Provider') diff --git a/g4f/Provider/BingCreateImages.py b/g4f/Provider/BingCreateImages.py index a7bd54e4..f29126b6 100644 --- a/g4f/Provider/BingCreateImages.py +++ b/g4f/Provider/BingCreateImages.py @@ -15,6 +15,7 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin): label = "Microsoft Designer" url = "https://www.bing.com/images/create" working = True + needs_auth = True def __init__(self, cookies: Cookies = None, proxy: str = None) -> None: self.cookies: Cookies = cookies diff --git a/g4f/Provider/Ecosia.py b/g4f/Provider/Ecosia.py index 1cae3560..231412aa 100644 --- a/g4f/Provider/Ecosia.py +++ b/g4f/Provider/Ecosia.py @@ -15,7 +15,8 @@ class Ecosia(AsyncGeneratorProvider, ProviderModelMixin): working = True supports_gpt_35_turbo = True default_model = "gpt-3.5-turbo-0125" - model_aliases = {"gpt-3.5-turbo": "gpt-3.5-turbo-0125"} + models = [default_model, "green"] + model_aliases = {"gpt-3.5-turbo": default_model} @classmethod async def create_async_generator( @@ -23,11 +24,10 @@ class Ecosia(AsyncGeneratorProvider, ProviderModelMixin): model: str, messages: Messages, connector: BaseConnector = None, - green: bool = False, proxy: str = None, **kwargs ) -> AsyncResult: - cls.get_model(model) + model = cls.get_model(model) headers = { "authority": "api.ecosia.org", "accept": "*/*", @@ -39,7 +39,7 @@ class Ecosia(AsyncGeneratorProvider, ProviderModelMixin): data = { "messages": base64.b64encode(json.dumps(messages).encode()).decode() } - api_url = f"https://api.ecosia.org/v2/chat/?sp={'eco' if green else 'productivity'}" + api_url = f"https://api.ecosia.org/v2/chat/?sp={'eco' if model == 'green' else 'productivity'}" async with session.post(api_url, json=data) as response: await raise_for_status(response) async for chunk in response.content.iter_any(): diff --git a/g4f/Provider/MetaAI.py b/g4f/Provider/MetaAI.py new file mode 100644 index 00000000..1831de63 --- /dev/null +++ b/g4f/Provider/MetaAI.py @@ -0,0 +1,199 @@ +import json +import uuid +import random +import time +import uuid +from typing import Dict, List + +from aiohttp import ClientSession, BaseConnector + +from ..typing import AsyncResult, Messages, Cookies +from ..requests import raise_for_status, DEFAULT_HEADERS +from ..image import ImageResponse +from .base_provider import AsyncGeneratorProvider +from .helper import format_prompt, get_connector, get_cookies + +class MetaAI(AsyncGeneratorProvider): + url = "https://www.meta.ai" + working = True + + def __init__(self, proxy: str = None, connector: BaseConnector = None): + self.session = ClientSession(connector=get_connector(connector, proxy), headers=DEFAULT_HEADERS) + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + proxy: str = None, + **kwargs + ) -> AsyncResult: + #cookies = get_cookies(".meta.ai", False, True) + async for chunk in cls(proxy).prompt(format_prompt(messages)): + yield chunk + + async def get_access_token(self, cookies: Cookies, birthday: str = "1999-01-01") -> str: + url = "https://www.meta.ai/api/graphql/" + + payload = { + "lsd": cookies["lsd"], + "fb_api_caller_class": "RelayModern", + "fb_api_req_friendly_name": "useAbraAcceptTOSForTempUserMutation", + "variables": json.dumps({ + "dob": birthday, + "icebreaker_type": "TEXT", + "__relay_internal__pv__WebPixelRatiorelayprovider": 1, + }), + "doc_id": "7604648749596940", + } + headers = { + "x-fb-friendly-name": "useAbraAcceptTOSForTempUserMutation", + "x-fb-lsd": cookies["lsd"], + "x-asbd-id": "129477", + "alt-used": "www.meta.ai", + "sec-fetch-site": "same-origin" + } + async with self.session.post(url, headers=headers, cookies=cookies, data=payload) as response: + await raise_for_status(response, "Fetch access_token failed") + 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"] + return access_token + + async def prompt(self, message: str, cookies: Cookies = None) -> AsyncResult: + access_token = None + if cookies is None: + cookies = await self.get_cookies() + access_token = await self.get_access_token(cookies) + else: + cookies = await self.get_cookies(cookies) + + url = "https://graph.meta.ai/graphql?locale=user" + #url = "https://www.meta.ai/api/graphql/" + payload = { + "access_token": access_token, + #"lsd": cookies["lsd"], + "fb_api_caller_class": "RelayModern", + "fb_api_req_friendly_name": "useAbraSendMessageMutation", + "variables": json.dumps({ + "message": {"sensitive_string_value": message}, + "externalConversationId": str(uuid.uuid4()), + "offlineThreadingId": generate_offline_threading_id(), + "suggestedPromptIndex": None, + "flashVideoRecapInput": {"images": []}, + "flashPreviewInput": None, + "promptPrefix": None, + "entrypoint": "ABRA__CHAT__TEXT", + "icebreaker_type": "TEXT", + "__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False, + "__relay_internal__pv__WebPixelRatiorelayprovider": 1, + }), + "server_timestamps": "true", + "doc_id": "7783822248314888", + } + headers = { + "x-asbd-id": "129477", + "x-fb-friendly-name": "useAbraSendMessageMutation", + #"x-fb-lsd": cookies["lsd"], + } + async with self.session.post(url, headers=headers, cookies=cookies, data=payload) as response: + await raise_for_status(response, "Fetch response failed") + last_snippet_len = 0 + fetch_id = None + async for line in response.content: + try: + json_line = json.loads(line) + except json.JSONDecodeError: + continue + bot_response_message = json_line.get("data", {}).get("node", {}).get("bot_response_message", {}) + streaming_state = bot_response_message.get("streaming_state") + fetch_id = bot_response_message.get("fetch_id") + if streaming_state in ("STREAMING", "OVERALL_DONE"): + #imagine_card = bot_response_message["imagine_card"] + snippet = bot_response_message["snippet"] + yield snippet[last_snippet_len:] + last_snippet_len = len(snippet) + elif streaming_state == "OVERALL_DONE": + break + #if last_streamed_response is None: + # if attempts > 3: + # raise Exception("MetaAI is having issues and was not able to respond (Server Error)") + # access_token = await self.get_access_token() + # return await self.prompt(message=message, attempts=attempts + 1) + if fetch_id is not None: + sources = await self.fetch_sources(fetch_id, cookies, access_token) + if sources is not None: + yield sources + + async def get_cookies(self, cookies: Cookies = None) -> dict: + async with self.session.get("https://www.meta.ai/", cookies=cookies) as response: + await raise_for_status(response, "Fetch home failed") + text = await response.text() + if cookies is None: + cookies = { + "_js_datr": self.extract_value(text, "_js_datr"), + "abra_csrf": self.extract_value(text, "abra_csrf"), + "datr": self.extract_value(text, "datr"), + } + cookies["lsd"] = self.extract_value(text, start_str='"LSD",[],{"token":"', end_str='"}') + return cookies + + async def fetch_sources(self, fetch_id: str, cookies: Cookies, access_token: str) -> List[Dict]: + url = "https://graph.meta.ai/graphql?locale=user" + payload = { + "access_token": access_token, + "fb_api_caller_class": "RelayModern", + "fb_api_req_friendly_name": "AbraSearchPluginDialogQuery", + "variables": json.dumps({"abraMessageFetchID": fetch_id}), + "server_timestamps": "true", + "doc_id": "6946734308765963", + } + headers = { + "authority": "graph.meta.ai", + "x-fb-friendly-name": "AbraSearchPluginDialogQuery", + } + async with self.session.post(url, headers=headers, cookies=cookies, data=payload) as response: + await raise_for_status(response) + response_json = await response.json() + try: + message = response_json["data"]["message"] + if message is not None: + searchResults = message["searchResults"] + if searchResults is not None: + return Sources(searchResults["references"]) + except (KeyError, TypeError): + raise RuntimeError(f"Response: {response_json}") + + @staticmethod + def extract_value(text: str, key: str = None, start_str = None, end_str = '",') -> str: + if start_str is None: + start_str = f'{key}":{{"value":"' + start = text.find(start_str) + if start >= 0: + start+= len(start_str) + end = text.find(end_str, start) + return text[start:end] + +def generate_offline_threading_id() -> str: + """ + Generates an offline threading ID. + + Returns: + str: The generated offline threading ID. + """ + # Generate a random 64-bit integer + random_value = random.getrandbits(64) + + # Get the current timestamp in milliseconds + timestamp = int(time.time() * 1000) + + # Combine timestamp and random value + threading_id = (timestamp << 22) | (random_value & ((1 << 22) - 1)) + + return str(threading_id) + +class Sources(): + def __init__(self, list: List[Dict[str, str]]) -> None: + self.list = list + + def __str__(self) -> str: + return "\n\n" + ("\n".join([f"[{link['title']}]({link['link']})" for link in self.list])) \ No newline at end of file diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index f761df5b..10249aa2 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -42,6 +42,7 @@ from .Koala import Koala from .Liaobots import Liaobots from .Llama import Llama from .Local import Local +from .MetaAI import MetaAI from .PerplexityLabs import PerplexityLabs from .Pi import Pi from .ReplicateImage import ReplicateImage diff --git a/g4f/Provider/base_provider.py b/g4f/Provider/base_provider.py index 4c0157f3..8f368747 100644 --- a/g4f/Provider/base_provider.py +++ b/g4f/Provider/base_provider.py @@ -1,3 +1,3 @@ from ..providers.base_provider import * -from ..providers.types import FinishReason +from ..providers.types import FinishReason, Streaming from .helper import get_cookies, format_prompt \ No newline at end of file diff --git a/g4f/Provider/needs_auth/Groq.py b/g4f/Provider/needs_auth/Groq.py index 922b2dd2..d11f6a82 100644 --- a/g4f/Provider/needs_auth/Groq.py +++ b/g4f/Provider/needs_auth/Groq.py @@ -4,7 +4,7 @@ from .Openai import Openai from ...typing import AsyncResult, Messages class Groq(Openai): - lebel = "Groq" + label = "Groq" url = "https://console.groq.com/playground" working = True default_model = "mixtral-8x7b-32768" diff --git a/g4f/Provider/needs_auth/OpenaiAccount.py b/g4f/Provider/needs_auth/OpenaiAccount.py index 5c90b1de..7be60c86 100644 --- a/g4f/Provider/needs_auth/OpenaiAccount.py +++ b/g4f/Provider/needs_auth/OpenaiAccount.py @@ -3,5 +3,4 @@ from __future__ import annotations from .OpenaiChat import OpenaiChat class OpenaiAccount(OpenaiChat): - label = "OpenAI ChatGPT with Account" needs_auth = True \ No newline at end of file -- cgit v1.2.3 From 83484c0a5658b023bcef930aee5099a4fc059cb4 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Sat, 20 Apr 2024 15:41:49 +0200 Subject: Add workers and use_colors options to api --- g4f/Provider/MetaAI.py | 71 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 34 deletions(-) (limited to 'g4f/Provider') diff --git a/g4f/Provider/MetaAI.py b/g4f/Provider/MetaAI.py index 1831de63..524214ad 100644 --- a/g4f/Provider/MetaAI.py +++ b/g4f/Provider/MetaAI.py @@ -9,9 +9,15 @@ from aiohttp import ClientSession, BaseConnector from ..typing import AsyncResult, Messages, Cookies from ..requests import raise_for_status, DEFAULT_HEADERS -from ..image import ImageResponse from .base_provider import AsyncGeneratorProvider -from .helper import format_prompt, get_connector, get_cookies +from .helper import format_prompt, get_connector + +class Sources(): + def __init__(self, list: List[Dict[str, str]]) -> None: + self.list = list + + def __str__(self) -> str: + return "\n\n" + ("\n".join([f"[{link['title']}]({link['link']})" for link in self.list])) class MetaAI(AsyncGeneratorProvider): url = "https://www.meta.ai" @@ -19,6 +25,8 @@ class MetaAI(AsyncGeneratorProvider): def __init__(self, proxy: str = None, connector: BaseConnector = None): self.session = ClientSession(connector=get_connector(connector, proxy), headers=DEFAULT_HEADERS) + self.cookies: Cookies = None + self.access_token: str = None @classmethod async def create_async_generator( @@ -32,11 +40,11 @@ class MetaAI(AsyncGeneratorProvider): async for chunk in cls(proxy).prompt(format_prompt(messages)): yield chunk - async def get_access_token(self, cookies: Cookies, birthday: str = "1999-01-01") -> str: + async def get_access_token(self, birthday: str = "1999-01-01") -> str: url = "https://www.meta.ai/api/graphql/" payload = { - "lsd": cookies["lsd"], + "lsd": self.lsd, "fb_api_caller_class": "RelayModern", "fb_api_req_friendly_name": "useAbraAcceptTOSForTempUserMutation", "variables": json.dumps({ @@ -48,29 +56,30 @@ class MetaAI(AsyncGeneratorProvider): } headers = { "x-fb-friendly-name": "useAbraAcceptTOSForTempUserMutation", - "x-fb-lsd": cookies["lsd"], + "x-fb-lsd": self.lsd, "x-asbd-id": "129477", "alt-used": "www.meta.ai", "sec-fetch-site": "same-origin" } - async with self.session.post(url, headers=headers, cookies=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") 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"] return access_token async def prompt(self, message: str, cookies: Cookies = None) -> AsyncResult: - access_token = None - if cookies is None: - cookies = await self.get_cookies() - access_token = await self.get_access_token(cookies) - else: - cookies = await self.get_cookies(cookies) + if cookies is not None: + self.cookies = cookies + self.access_token = None + if self.cookies is None: + self.cookies = await self.get_cookies() + if self.access_token is None: + self.access_token = await self.get_access_token() url = "https://graph.meta.ai/graphql?locale=user" #url = "https://www.meta.ai/api/graphql/" payload = { - "access_token": access_token, + "access_token": self.access_token, #"lsd": cookies["lsd"], "fb_api_caller_class": "RelayModern", "fb_api_req_friendly_name": "useAbraSendMessageMutation", @@ -95,7 +104,7 @@ class MetaAI(AsyncGeneratorProvider): "x-fb-friendly-name": "useAbraSendMessageMutation", #"x-fb-lsd": cookies["lsd"], } - async with self.session.post(url, headers=headers, cookies=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 response failed") last_snippet_len = 0 fetch_id = None @@ -106,25 +115,25 @@ class MetaAI(AsyncGeneratorProvider): continue bot_response_message = json_line.get("data", {}).get("node", {}).get("bot_response_message", {}) streaming_state = bot_response_message.get("streaming_state") - fetch_id = bot_response_message.get("fetch_id") + fetch_id = bot_response_message.get("fetch_id") or fetch_id if streaming_state in ("STREAMING", "OVERALL_DONE"): #imagine_card = bot_response_message["imagine_card"] snippet = bot_response_message["snippet"] - yield snippet[last_snippet_len:] - last_snippet_len = len(snippet) - elif streaming_state == "OVERALL_DONE": - break + new_snippet_len = len(snippet) + if new_snippet_len > last_snippet_len: + yield snippet[last_snippet_len:] + last_snippet_len = new_snippet_len #if last_streamed_response is None: # if attempts > 3: # raise Exception("MetaAI is having issues and was not able to respond (Server Error)") # access_token = await self.get_access_token() # return await self.prompt(message=message, attempts=attempts + 1) if fetch_id is not None: - sources = await self.fetch_sources(fetch_id, cookies, access_token) + sources = await self.fetch_sources(fetch_id) if sources is not None: yield sources - async def get_cookies(self, cookies: Cookies = None) -> dict: + async def get_cookies(self, cookies: Cookies = None) -> Cookies: async with self.session.get("https://www.meta.ai/", cookies=cookies) as response: await raise_for_status(response, "Fetch home failed") text = await response.text() @@ -134,13 +143,13 @@ class MetaAI(AsyncGeneratorProvider): "abra_csrf": self.extract_value(text, "abra_csrf"), "datr": self.extract_value(text, "datr"), } - cookies["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 - async def fetch_sources(self, fetch_id: str, cookies: Cookies, access_token: str) -> List[Dict]: + async def fetch_sources(self, fetch_id: str) -> Sources: url = "https://graph.meta.ai/graphql?locale=user" payload = { - "access_token": access_token, + "access_token": self.access_token, "fb_api_caller_class": "RelayModern", "fb_api_req_friendly_name": "AbraSearchPluginDialogQuery", "variables": json.dumps({"abraMessageFetchID": fetch_id}), @@ -151,7 +160,7 @@ class MetaAI(AsyncGeneratorProvider): "authority": "graph.meta.ai", "x-fb-friendly-name": "AbraSearchPluginDialogQuery", } - async with self.session.post(url, headers=headers, cookies=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) response_json = await response.json() try: @@ -171,7 +180,8 @@ class MetaAI(AsyncGeneratorProvider): if start >= 0: start+= len(start_str) end = text.find(end_str, start) - return text[start:end] + if end >= 0: + return text[start:end] def generate_offline_threading_id() -> str: """ @@ -189,11 +199,4 @@ def generate_offline_threading_id() -> str: # Combine timestamp and random value threading_id = (timestamp << 22) | (random_value & ((1 << 22) - 1)) - return str(threading_id) - -class Sources(): - def __init__(self, list: List[Dict[str, str]]) -> None: - self.list = list - - def __str__(self) -> str: - return "\n\n" + ("\n".join([f"[{link['title']}]({link['link']})" for link in self.list])) \ No newline at end of file + return str(threading_id) \ No newline at end of file -- cgit v1.2.3 From e6863a75e1a495794fa728482389c859a73aea98 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Sat, 20 Apr 2024 18:04:16 +0200 Subject: Improve cli arguments --- g4f/Provider/MetaAI.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'g4f/Provider') diff --git a/g4f/Provider/MetaAI.py b/g4f/Provider/MetaAI.py index 524214ad..d4cca68d 100644 --- a/g4f/Provider/MetaAI.py +++ b/g4f/Provider/MetaAI.py @@ -2,7 +2,6 @@ import json import uuid import random import time -import uuid from typing import Dict, List from aiohttp import ClientSession, BaseConnector @@ -13,8 +12,8 @@ from .base_provider import AsyncGeneratorProvider from .helper import format_prompt, get_connector class Sources(): - def __init__(self, list: List[Dict[str, str]]) -> None: - self.list = list + def __init__(self, link_list: List[Dict[str, str]]) -> None: + self.link = link_list def __str__(self) -> str: return "\n\n" + ("\n".join([f"[{link['title']}]({link['link']})" for link in self.list])) -- cgit v1.2.3 From a129c3db44eaab420086c5fd9817ee03f5c26d59 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Sat, 20 Apr 2024 18:21:19 +0200 Subject: Add AbraGeoBlockedError handling --- g4f/Provider/MetaAI.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'g4f/Provider') diff --git a/g4f/Provider/MetaAI.py b/g4f/Provider/MetaAI.py index d4cca68d..e64a96d5 100644 --- a/g4f/Provider/MetaAI.py +++ b/g4f/Provider/MetaAI.py @@ -18,6 +18,9 @@ class Sources(): def __str__(self) -> str: return "\n\n" + ("\n".join([f"[{link['title']}]({link['link']})" for link in self.list])) +class AbraGeoBlockedError(Exception): + pass + class MetaAI(AsyncGeneratorProvider): url = "https://www.meta.ai" working = True @@ -136,6 +139,8 @@ class MetaAI(AsyncGeneratorProvider): async with self.session.get("https://www.meta.ai/", cookies=cookies) as response: await raise_for_status(response, "Fetch home failed") text = await response.text() + if "AbraGeoBlockedError" in text: + raise AbraGeoBlockedError("Meta AI isn't available yet in your country") if cookies is None: cookies = { "_js_datr": self.extract_value(text, "_js_datr"), -- cgit v1.2.3