From 9b19603f037cb62ca4d81c68d7e11d5dec0ff969 Mon Sep 17 00:00:00 2001 From: razrab Date: Thu, 16 Nov 2023 11:54:25 +0300 Subject: [PATCH 01/33] Fixed tupo in list_ignored_providers parameter. --- g4f/api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py index 166395db9ad..d8798ef2573 100644 --- a/g4f/api/__init__.py +++ b/g4f/api/__init__.py @@ -86,7 +86,7 @@ async def chat_completions(request: Request, item: JSONStructure = None): model=model, stream=stream, messages=messages, - list_ignored_providers=self.list_ignored_providers) + ignored=self.list_ignored_providers) except Exception as e: logging.exception(e) return Response(content=json.dumps({"error": "An error occurred while generating the response."}, indent=4), media_type="application/json") From 1906b5fd06917f745c0a2f68c7f2897954be2bc3 Mon Sep 17 00:00:00 2001 From: razrab Date: Thu, 16 Nov 2023 12:05:53 +0300 Subject: [PATCH 02/33] Add back crutch of incorrect responses to ChatBase --- g4f/Provider/ChatBase.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/g4f/Provider/ChatBase.py b/g4f/Provider/ChatBase.py index 9f11e1acd65..ccc20244990 100644 --- a/g4f/Provider/ChatBase.py +++ b/g4f/Provider/ChatBase.py @@ -12,6 +12,8 @@ class ChatBase(AsyncGeneratorProvider): supports_message_history = True working = True jailbreak = True + list_incorrect_responses = ["support@chatbase", + "about Chatbase"] @classmethod async def create_async_generator( @@ -53,6 +55,9 @@ async def create_async_generator( response_data = "" async for stream in response.content.iter_any(): response_data += stream.decode() + for incorrect_response in cls.list_incorrect_responses: + if incorrect_response in response_data: + raise RuntimeError("Incorrect response") yield stream.decode() @classmethod From 0c4e5e5127f91c6789da8dbfbcbfee63d7a578a3 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Thu, 16 Nov 2023 16:56:23 +0100 Subject: [PATCH 03/33] Fix Phind and PerplexityAi - GPT-4 Providers Fix MyShell Provider Refactor Provider __init__ Add ChatAnywhere Provider Update models list --- g4f/Provider/ChatAnywhere.py | 53 ++++++++ g4f/Provider/MyShell.py | 147 +++++++++++---------- g4f/Provider/PerplexityAi.py | 121 +++++++++++++++++ g4f/Provider/Phind.py | 165 +++++++++++++---------- g4f/Provider/__init__.py | 168 ++++-------------------- g4f/Provider/helper.py | 57 ++++++-- g4f/Provider/unfinished/PerplexityAi.py | 100 -------------- g4f/Provider/unfinished/__init__.py | 1 - g4f/models.py | 25 ++-- 9 files changed, 435 insertions(+), 402 deletions(-) create mode 100644 g4f/Provider/ChatAnywhere.py create mode 100644 g4f/Provider/PerplexityAi.py delete mode 100644 g4f/Provider/unfinished/PerplexityAi.py diff --git a/g4f/Provider/ChatAnywhere.py b/g4f/Provider/ChatAnywhere.py new file mode 100644 index 00000000000..704544e979a --- /dev/null +++ b/g4f/Provider/ChatAnywhere.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from aiohttp import ClientSession + +from ..typing import AsyncResult, Messages +from .base_provider import AsyncGeneratorProvider + + +class ChatAnywhere(AsyncGeneratorProvider): + url = "https://chatanywhere.cn" + supports_gpt_35_turbo = True + supports_message_history = True + working = True + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + proxy: str = None, + temperature: float = 0.5, + **kwargs + ) -> AsyncResult: + headers = { + "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0", + "Accept": "application/json, text/plain, */*", + "Accept-Language": "de,en-US;q=0.7,en;q=0.3", + "Accept-Encoding": "gzip, deflate, br", + "Content-Type": "application/json", + "Referer": f"{cls.url}/", + "Origin": cls.url, + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + "Authorization": "", + "Connection": "keep-alive", + "TE": "trailers" + } + async with ClientSession(headers=headers) as session: + data = { + "list": messages, + "id": "s1_qYuOLXjI3rEpc7WHfQ", + "title": messages[-1]["content"], + "prompt": "", + "temperature": temperature, + "models": "61490748", + "continuous": True + } + async with session.post(f"{cls.url}/v1/chat/gpt/", json=data, proxy=proxy) as response: + response.raise_for_status() + async for chunk in response.content.iter_any(): + if chunk: + yield chunk.decode() \ No newline at end of file diff --git a/g4f/Provider/MyShell.py b/g4f/Provider/MyShell.py index 02b273542d4..09f6feb98bc 100644 --- a/g4f/Provider/MyShell.py +++ b/g4f/Provider/MyShell.py @@ -1,91 +1,94 @@ from __future__ import annotations -import time, random, json +import time, json -from ..requests import StreamSession -from ..typing import AsyncResult, Messages -from .base_provider import AsyncGeneratorProvider -from .helper import format_prompt +try: + from selenium.webdriver.remote.webdriver import WebDriver +except ImportError: + class WebDriver(): + pass -class MyShell(AsyncGeneratorProvider): +from ..typing import CreateResult, Messages +from .base_provider import BaseProvider +from .helper import format_prompt, get_browser + +class MyShell(BaseProvider): url = "https://app.myshell.ai/chat" working = True supports_gpt_35_turbo = True + supports_stream = True @classmethod - async def create_async_generator( + def create_completion( cls, model: str, messages: Messages, + stream: bool, proxy: str = None, timeout: int = 120, + browser: WebDriver = None, + display: bool = True, **kwargs - ) -> AsyncResult: - user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" - headers = { - "User-Agent": user_agent, - "Myshell-Service-Name": "organics-api", - "Visitor-Id": generate_visitor_id(user_agent) - } - async with StreamSession( - impersonate="chrome107", - proxies={"https": proxy}, - timeout=timeout, - headers=headers - ) as session: - prompt = format_prompt(messages) + ) -> CreateResult: + if not browser: + if display: + driver, display = get_browser("", True, proxy) + else: + display = get_browser("", False, proxy) + else: + driver = browser + + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + + driver.get(cls.url) + try: + WebDriverWait(driver, timeout).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "body:not(.no-js)")) + ) + script = """ +response = await fetch("https://api.myshell.ai/v1/bot/chat/send_message", { + "headers": { + "accept": "application/json", + "content-type": "application/json", + "myshell-service-name": "organics-api", + "visitor-id": localStorage.getItem("mix_visitorId") + }, + "body": '{body}', + "method": "POST" +}) +window.reader = response.body.getReader(); +""" data = { - "botId": "1", + "botId": "4738", "conversation_scenario": 3, - "message": prompt, + "message": format_prompt(messages), "messageType": 1 } - async with session.post("https://api.myshell.ai/v1/bot/chat/send_message", json=data) as response: - response.raise_for_status() - event = None - async for line in response.iter_lines(): - if line.startswith(b"event: "): - event = line[7:] - elif event == b"MESSAGE_REPLY_SSE_ELEMENT_EVENT_NAME_TEXT": - if line.startswith(b"data: "): - yield json.loads(line[6:])["content"] - if event == b"MESSAGE_REPLY_SSE_ELEMENT_EVENT_NAME_TEXT_STREAM_PUSH_FINISHED": - break - - -def xor_hash(B: str): - r = [] - i = 0 - - def o(e, t): - o_val = 0 - for i in range(len(t)): - o_val |= r[i] << (8 * i) - return e ^ o_val - - for e in range(len(B)): - t = ord(B[e]) - r.insert(0, 255 & t) - - if len(r) >= 4: - i = o(i, r) - r = [] - - if len(r) > 0: - i = o(i, r) - - return hex(i)[2:] - -def performance() -> str: - t = int(time.time() * 1000) - e = 0 - while t == int(time.time() * 1000): - e += 1 - return hex(t)[2:] + hex(e)[2:] - -def generate_visitor_id(user_agent: str) -> str: - f = performance() - r = hex(int(random.random() * (16**16)))[2:-2] - d = xor_hash(user_agent) - e = hex(1080 * 1920)[2:] - return f"{f}-{r}-{d}-{e}-{f}" \ No newline at end of file + driver.execute_script(script.replace("{body}", json.dumps(data))) + script = """ +chunk = await window.reader.read(); +text = await (new Response(chunk['value']).text()); +content = ''; +text.split('\\n').forEach((line, index) => { + if (line.startsWith('data: ')) { + try { + const data = JSON.parse(line.substring('data: '.length)); + if ('content' in data) { + content += data['content']; + } + } catch(e) {} + } +}); +return content; +""" + while chunk := driver.execute_script(script): + yield chunk + finally: + driver.close() + if not browser: + time.sleep(0.1) + driver.quit() + if display: + display.stop() \ No newline at end of file diff --git a/g4f/Provider/PerplexityAi.py b/g4f/Provider/PerplexityAi.py new file mode 100644 index 00000000000..8bf83b6ab1f --- /dev/null +++ b/g4f/Provider/PerplexityAi.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +import time +try: + from selenium.webdriver.remote.webdriver import WebDriver +except ImportError: + class WebDriver(): + pass + +from ..typing import CreateResult, Messages +from .base_provider import BaseProvider +from .helper import format_prompt, get_browser + +class PerplexityAi(BaseProvider): + url = "https://www.perplexity.ai" + working = True + supports_gpt_4 = True + supports_stream = True + + @classmethod + def create_completion( + cls, + model: str, + messages: Messages, + stream: bool, + proxy: str = None, + timeout: int = 120, + browser: WebDriver = None, + copilot: bool = False, + display: bool = True, + **kwargs + ) -> CreateResult: + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + + if browser: + driver = browser + else: + if display: + driver, display = get_browser("", True, proxy) + else: + driver = get_browser("", False, proxy) + + prompt = format_prompt(messages) + + driver.get(f"{cls.url}/") + wait = WebDriverWait(driver, timeout) + + script = """ +window._message = window._last_message = ""; +window._message_finished = false; +const _socket_send = WebSocket.prototype.send; +WebSocket.prototype.send = function(...args) { + if (!window.socket_onmessage) { + window._socket_onmessage = this; + this.addEventListener("message", (event) => { + if (event.data.startsWith("42")) { + let data = JSON.parse(event.data.substring(2)); + if (data[0] =="query_progress" || data[0] == "query_answered") { + let content = JSON.parse(data[1]["text"]); + if (data[1]["mode"] == "copilot") { + content = content[content.length-1]["content"]["answer"]; + content = JSON.parse(content); + } + window._message = content["answer"]; + window._message_finished = data[0] == "query_answered"; + window._web_results = content["web_results"]; + } + } + }); + } + return _socket_send.call(this, ...args); +}; +""" + driver.execute_script(script) + + # Page loaded? + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']"))) + + if copilot: + try: + driver.find_element(By.CSS_SELECTOR, "img[alt='User avatar']") + driver.find_element(By.CSS_SELECTOR, "button[data-testid='copilot-toggle']").click() + except: + pass + + # Enter question + driver.find_element(By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']").send_keys(prompt) + # Submit question + driver.find_element(By.CSS_SELECTOR, "button.bg-super svg[data-icon='arrow-right']").click() + + try: + script = """ +if(window._message && window._message != window._last_message) { + try { + return window._message.substring(window._last_message.length); + } finally { + window._last_message = window._message; + } +} else if(window._message_finished) { + return null; +} else { + return ''; +} +""" + while True: + chunk = driver.execute_script(script) + if chunk: + yield chunk + elif chunk != "": + break + else: + time.sleep(0.1) + finally: + driver.close() + if not browser: + time.sleep(0.1) + driver.quit() + if display: + display.stop() \ No newline at end of file diff --git a/g4f/Provider/Phind.py b/g4f/Provider/Phind.py index 8f3e794205a..56100d87cf1 100644 --- a/g4f/Provider/Phind.py +++ b/g4f/Provider/Phind.py @@ -1,83 +1,116 @@ from __future__ import annotations -import random, string -from datetime import datetime +import time +from urllib.parse import quote +try: + from selenium.webdriver.remote.webdriver import WebDriver +except ImportError: + class WebDriver(): + pass -from ..typing import AsyncResult, Messages -from ..requests import StreamSession -from .base_provider import AsyncGeneratorProvider, format_prompt +from ..typing import CreateResult, Messages +from .base_provider import BaseProvider +from .helper import format_prompt, get_browser - -class Phind(AsyncGeneratorProvider): +class Phind(BaseProvider): url = "https://www.phind.com" working = True supports_gpt_4 = True + supports_stream = True @classmethod - async def create_async_generator( + def create_completion( cls, model: str, messages: Messages, + stream: bool, proxy: str = None, timeout: int = 120, + browser: WebDriver = None, + creative_mode: bool = None, + display: bool = True, **kwargs - ) -> AsyncResult: - chars = string.ascii_lowercase + string.digits - user_id = ''.join(random.choice(chars) for _ in range(24)) - data = { - "question": format_prompt(messages), - "webResults": [], - "options": { - "date": datetime.now().strftime("%d.%m.%Y"), - "language": "en", - "detailed": True, - "anonUserId": user_id, - "answerModel": "GPT-4", - "creativeMode": False, - "customLinks": [] - }, - "context":"" - } - headers = { - "Authority": cls.url, - "Accept": "application/json, text/plain, */*", - "Origin": cls.url, - "Referer": f"{cls.url}/" - } - async with StreamSession( - headers=headers, - timeout=(5, timeout), - proxies={"https": proxy}, - impersonate="chrome107" - ) as session: - async with session.post(f"{cls.url}/api/infer/answer", json=data) as response: - response.raise_for_status() - new_lines = 0 - async for line in response.iter_lines(): - if not line: - continue - if line.startswith(b"data: "): - line = line[6:] - if line.startswith(b""): - continue - if line: - if new_lines: - yield "".join(["\n" for _ in range(int(new_lines / 2))]) - new_lines = 0 - yield line.decode() - else: - new_lines += 1 + ) -> CreateResult: + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + if browser: + driver = browser + else: + if display: + driver, display = get_browser("", True, proxy) + else: + driver = get_browser("", False, proxy) - @classmethod - @property - def params(cls): - params = [ - ("model", "str"), - ("messages", "list[dict[str, str]]"), - ("stream", "bool"), - ("proxy", "str"), - ("timeout", "int"), - ] - param = ", ".join([": ".join(p) for p in params]) - return f"g4f.provider.{cls.__name__} supports: ({param})" \ No newline at end of file + prompt = quote(format_prompt(messages)) + driver.get(f"{cls.url}/search?q={prompt}&source=searchbox") + + if model.startswith("gpt-4") or creative_mode: + wait = WebDriverWait(driver, timeout) + # Open dropdown + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "button.text-dark.dropdown-toggle"))) + driver.find_element(By.CSS_SELECTOR, "button.text-dark.dropdown-toggle").click() + # Enable GPT-4 + wait.until(EC.visibility_of_element_located((By.XPATH, "//button[text()='GPT-4']"))) + if model.startswith("gpt-4"): + driver.find_element(By.XPATH, "//button[text()='GPT-4']").click() + # Enable creative mode + if creative_mode or creative_mode == None: + driver.find_element(By.ID, "Creative Mode").click() + # Submit question + driver.find_element(By.CSS_SELECTOR, ".search-bar-input-group button[type='submit']").click() + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".search-container"))) + + try: + script = """ +window._fetch = window.fetch; +window.fetch = (url, options) => { + const result = window._fetch(url, options); + if (url != "/api/infer/answer") return result; + result.then((response) => { + if (!response.body.locked) { + window.reader = response.body.getReader(); + } + }); + return new Promise((resolve, reject) => { + resolve(new Response(new ReadableStream())) + }); +} +""" + driver.execute_script(script) + script = """ +if(window.reader) { + chunk = await window.reader.read(); + if (chunk['done']) return null; + text = await (new Response(chunk['value']).text()); + content = ''; + text.split('\\r\\n').forEach((line, index) => { + if (line.startsWith('data: ')) { + line = line.substring('data: '.length); + if (!line.startsWith('')) { + if (line) content += line; + else content += '\\n'; + } + } + }); + return content.replace('\\n\\n', '\\n'); +} else { + return '' +} +""" + while True: + chunk = driver.execute_script(script) + if chunk: + yield chunk + elif chunk != "": + break + else: + time.sleep(0.1) + finally: + driver.close() + if not browser: + time.sleep(0.1) + driver.quit() + if display: + display.stop() \ No newline at end of file diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index 70ad9de72b9..f1f0a1670cd 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -1,10 +1,12 @@ -from __future__ import annotations +from __future__ import annotations + from .AiAsk import AiAsk from .Aichat import Aichat from .AItianhu import AItianhu from .AItianhuSpace import AItianhuSpace from .Berlin import Berlin from .Bing import Bing +from .ChatAnywhere import ChatAnywhere from .ChatBase import ChatBase from .ChatForAi import ChatForAi from .Chatgpt4Online import Chatgpt4Online @@ -28,6 +30,7 @@ from .MyShell import MyShell from .NoowAi import NoowAi from .Opchatgpts import Opchatgpts +from .PerplexityAi import PerplexityAi from .Phind import Phind from .Vercel import Vercel from .Ylokh import Ylokh @@ -41,150 +44,23 @@ from .needs_auth import * from .unfinished import * -class ProviderUtils: - convert: dict[str, BaseProvider] = { - 'AItianhu': AItianhu, - 'AItianhuSpace': AItianhuSpace, - 'Acytoo': Acytoo, - 'AiAsk': AiAsk, - 'AiService': AiService, - 'Aibn': Aibn, - 'Aichat': Aichat, - 'Ails': Ails, - 'Aivvm': Aivvm, - 'AsyncGeneratorProvider': AsyncGeneratorProvider, - 'AsyncProvider': AsyncProvider, - 'Bard': Bard, - 'BaseProvider': BaseProvider, - 'Berlin': Berlin, - 'Bing': Bing, - 'ChatBase': ChatBase, - 'ChatForAi': ChatForAi, - 'Chatgpt4Online': Chatgpt4Online, - 'ChatgptAi': ChatgptAi, - 'ChatgptDemo': ChatgptDemo, - 'ChatgptDuo': ChatgptDuo, - 'ChatgptFree': ChatgptFree, - 'ChatgptLogin': ChatgptLogin, - 'ChatgptX': ChatgptX, - 'CodeLinkAva': CodeLinkAva, - 'Cromicle': Cromicle, - 'DeepInfra': DeepInfra, - 'DfeHub': DfeHub, - 'EasyChat': EasyChat, - 'Equing': Equing, - 'FastGpt': FastGpt, - 'Forefront': Forefront, - 'FakeGpt': FakeGpt, - 'FreeGpt': FreeGpt, - 'GPTalk': GPTalk, - 'GptChatly': GptChatly, - 'GetGpt': GetGpt, - 'GptForLove': GptForLove, - 'GptGo': GptGo, - 'GptGod': GptGod, - 'Hashnode': Hashnode, - 'H2o': H2o, - 'HuggingChat': HuggingChat, - 'Komo': Komo, - 'Koala': Koala, - 'Liaobots': Liaobots, - 'Llama2': Llama2, - 'Lockchat': Lockchat, - 'MikuChat': MikuChat, - 'Myshell': Myshell, - 'MyShell': MyShell, - 'NoowAi': NoowAi, - 'Opchatgpts': Opchatgpts, - 'OpenAssistant': OpenAssistant, - 'OpenaiChat': OpenaiChat, - 'PerplexityAi': PerplexityAi, - 'Phind': Phind, - 'Raycast': Raycast, - 'Theb': Theb, - 'V50': V50, - 'Vercel': Vercel, - 'Vitalentum': Vitalentum, - 'Wewordle': Wewordle, - 'Wuguokai': Wuguokai, - 'Ylokh': Ylokh, - 'You': You, - 'Yqcloud': Yqcloud, - 'GeekGpt': GeekGpt, - - 'BaseProvider': BaseProvider, - 'AsyncProvider': AsyncProvider, - 'AsyncGeneratorProvider': AsyncGeneratorProvider, - 'RetryProvider': RetryProvider, - } +import sys -__all__ = [ - 'BaseProvider', - 'AsyncProvider', - 'AsyncGeneratorProvider', - 'RetryProvider', - 'Acytoo', - 'AiAsk', - 'Aibn', - 'Aichat', - 'Ails', - 'Aivvm', - 'AiService', - 'AItianhu', - 'AItianhuSpace', - 'Aivvm', - 'Bard', - 'Berlin', - 'Bing', - 'ChatBase', - 'ChatForAi', - 'Chatgpt4Online', - 'ChatgptAi', - 'ChatgptDemo', - 'ChatgptDuo', - 'ChatgptFree', - 'ChatgptLogin', - 'ChatgptX', - 'Cromicle', - 'DeepInfra', - 'CodeLinkAva', - 'DfeHub', - 'EasyChat', - 'Forefront', - 'FakeGpt', - 'FreeGpt', - 'GPTalk', - 'GptChatly', - 'GptForLove', - 'GetGpt', - 'GptGo', - 'GptGod', - 'Hashnode', - 'H2o', - 'HuggingChat', - 'Koala', - 'Liaobots', - 'Llama2', - 'Lockchat', - 'Myshell', - 'MyShell', - 'NoowAi', - 'Opchatgpts', - 'Raycast', - 'OpenaiChat', - 'OpenAssistant', - 'PerplexityAi', - 'Phind', - 'Theb', - 'Vercel', - 'Vitalentum', - 'Wewordle', - 'Ylokh', - 'You', - 'Yqcloud', - 'Equing', - 'FastGpt', - 'Wuguokai', - 'V50', - 'GeekGpt' +__modules__: list = [ + getattr(sys.modules[__name__], provider) for provider in dir() + if not provider.startswith("__") +] +__providers__: list[type[BaseProvider]] = [ + provider for provider in __modules__ + if isinstance(provider, type) + and issubclass(provider, BaseProvider) ] +__all__: list[str] = [ + provider.__name__ for provider in __providers__ +] +__map__: dict[str, BaseProvider] = dict([ + (provider.__name__, provider) for provider in __providers__ +]) + +class ProviderUtils: + convert: dict[str, BaseProvider] = __map__ \ No newline at end of file diff --git a/g4f/Provider/helper.py b/g4f/Provider/helper.py index 519a024a0d8..cd9a971cca1 100644 --- a/g4f/Provider/helper.py +++ b/g4f/Provider/helper.py @@ -3,13 +3,39 @@ import sys import asyncio import webbrowser - from os import path from asyncio import AbstractEventLoop from platformdirs import user_config_dir +from browser_cookie3 import ( + chrome, + chromium, + opera, + opera_gx, + brave, + edge, + vivaldi, + firefox, + BrowserCookieError +) +try: + from undetected_chromedriver import Chrome, ChromeOptions +except ImportError: + class Chrome(): + def __init__(): + raise RuntimeError('Please install "undetected_chromedriver" and "pyvirtualdisplay" package') + class ChromeOptions(): + def add_argument(): + pass +try: + from pyvirtualdisplay import Display +except ImportError: + class Display(): + def start(): + pass + def stop(): + pass -from ..typing import Dict, Messages -from browser_cookie3 import chrome, chromium, opera, opera_gx, brave, edge, vivaldi, firefox, BrowserCookieError +from ..typing import Dict, Messages, Union, Tuple from .. import debug # Change event loop policy on windows @@ -106,10 +132,25 @@ def format_prompt(messages: Messages, add_special_tokens=False) -> str: return f"{formatted}\nAssistant:" -def get_browser(user_data_dir: str = None): - from undetected_chromedriver import Chrome - - if not user_data_dir: +def get_browser( + user_data_dir: str = None, + display: bool = False, + proxy: str = None +) -> Union[Chrome, Tuple[Chrome, Display]] : + if user_data_dir == None: user_data_dir = user_config_dir("g4f") - return Chrome(user_data_dir=user_data_dir) \ No newline at end of file + if display: + display = Display(visible=0, size=(1920, 1080)) + display.start() + + options = None + if proxy: + options = ChromeOptions() + options.add_argument(f'--proxy-server={proxy}') + + browser = Chrome(user_data_dir=user_data_dir, options=options) + if display: + return browser, display + + return browser \ No newline at end of file diff --git a/g4f/Provider/unfinished/PerplexityAi.py b/g4f/Provider/unfinished/PerplexityAi.py deleted file mode 100644 index e97dbf0d49f..00000000000 --- a/g4f/Provider/unfinished/PerplexityAi.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -import json -import time -import base64 -from curl_cffi.requests import AsyncSession - -from ..base_provider import AsyncProvider, format_prompt, get_cookies - - -class PerplexityAi(AsyncProvider): - url = "https://www.perplexity.ai" - supports_gpt_35_turbo = True - _sources = [] - - @classmethod - async def create_async( - cls, - model: str, - messages: list[dict[str, str]], - proxy: str = None, - **kwargs - ) -> str: - url = f"{cls.url}/socket.io/?EIO=4&transport=polling" - headers = { - "Referer": f"{cls.url}/" - } - async with AsyncSession(headers=headers, proxies={"https": proxy}, impersonate="chrome107") as session: - url_session = "https://www.perplexity.ai/api/auth/session" - response = await session.get(url_session) - response.raise_for_status() - - url_session = "https://www.perplexity.ai/api/auth/session" - response = await session.get(url_session) - response.raise_for_status() - - response = await session.get(url, params={"t": timestamp()}) - response.raise_for_status() - sid = json.loads(response.text[1:])["sid"] - - response = await session.get(url, params={"t": timestamp(), "sid": sid}) - response.raise_for_status() - - data = '40{"jwt":"anonymous-ask-user"}' - response = await session.post(url, params={"t": timestamp(), "sid": sid}, data=data) - response.raise_for_status() - - response = await session.get(url, params={"t": timestamp(), "sid": sid}) - response.raise_for_status() - - data = "424" + json.dumps([ - "perplexity_ask", - format_prompt(messages), - { - "version":"2.1", - "source":"default", - "language":"en", - "timezone": time.tzname[0], - "search_focus":"internet", - "mode":"concise" - } - ]) - response = await session.post(url, params={"t": timestamp(), "sid": sid}, data=data) - response.raise_for_status() - - while True: - response = await session.get(url, params={"t": timestamp(), "sid": sid}) - response.raise_for_status() - for line in response.text.splitlines(): - if line.startswith("434"): - result = json.loads(json.loads(line[3:])[0]["text"]) - - cls._sources = [{ - "title": source["name"], - "url": source["url"], - "snippet": source["snippet"] - } for source in result["web_results"]] - - return result["answer"] - - @classmethod - def get_sources(cls): - return cls._sources - - - @classmethod - @property - def params(cls): - params = [ - ("model", "str"), - ("messages", "list[dict[str, str]]"), - ("stream", "bool"), - ("proxy", "str"), - ] - param = ", ".join([": ".join(p) for p in params]) - return f"g4f.provider.{cls.__name__} supports: ({param})" - - -def timestamp() -> str: - return base64.urlsafe_b64encode(int(time.time()-1407782612).to_bytes(4, 'big')).decode() \ No newline at end of file diff --git a/g4f/Provider/unfinished/__init__.py b/g4f/Provider/unfinished/__init__.py index bf5ff9aa135..712e6212044 100644 --- a/g4f/Provider/unfinished/__init__.py +++ b/g4f/Provider/unfinished/__init__.py @@ -1,5 +1,4 @@ from .MikuChat import MikuChat -from .PerplexityAi import PerplexityAi from .Komo import Komo from .TalkAi import TalkAi from .ChatAiGpt import ChatAiGpt \ No newline at end of file diff --git a/g4f/models.py b/g4f/models.py index cdca0e3fa2e..868bf5b274a 100644 --- a/g4f/models.py +++ b/g4f/models.py @@ -3,6 +3,9 @@ from .typing import Union from .Provider import BaseProvider, RetryProvider from .Provider import ( + Chatgpt4Online, + ChatAnywhere, + PerplexityAi, GptForLove, ChatgptAi, DeepInfra, @@ -11,14 +14,12 @@ GeekGpt, FakeGpt, FreeGpt, - NoowAi, Berlin, Llama2, Vercel, - GPTalk, + Phind, Koala, GptGo, - Phind, Bard, Bing, You, @@ -39,20 +40,24 @@ def __all__() -> list[str]: name = "", base_provider = "", best_provider = RetryProvider([ - Bing, # Not fully GPT 3 or 4 + Bing, ChatgptAi, GptGo, GeekGpt, - Phind, You + You, + Chatgpt4Online, + ChatAnywhere, ]) ) -# GPT-3.5 too, but all providers supports long responses and a custom timeouts +# GPT-3.5 too, but all providers supports long requests and responses gpt_35_long = Model( name = 'gpt-3.5-turbo', base_provider = 'openai', best_provider = RetryProvider([ FreeGpt, You, GeekGpt, FakeGpt, - Berlin, Koala + Berlin, Koala, + Chatgpt4Online, + ChatAnywhere, ]) ) @@ -62,7 +67,9 @@ def __all__() -> list[str]: base_provider = 'openai', best_provider=RetryProvider([ ChatgptX, GptGo, You, - NoowAi, GPTalk, GptForLove, Phind, ChatBase + GptForLove, ChatBase, + Chatgpt4Online, + ChatAnywhere, ]) ) @@ -70,7 +77,7 @@ def __all__() -> list[str]: name = 'gpt-4', base_provider = 'openai', best_provider = RetryProvider([ - Bing, GeekGpt, Phind + Bing, Phind, PerplexityAi ]) ) From 510f534c0a3cc6219b3b842431b51f505dc3032e Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Thu, 16 Nov 2023 18:10:19 +0100 Subject: [PATCH 04/33] Fix Bard Provider --- g4f/Provider/PerplexityAi.py | 16 ++-- g4f/Provider/needs_auth/Bard.py | 148 +++++++++++++++++--------------- g4f/models.py | 3 +- 3 files changed, 86 insertions(+), 81 deletions(-) diff --git a/g4f/Provider/PerplexityAi.py b/g4f/Provider/PerplexityAi.py index 8bf83b6ab1f..ebb004060c1 100644 --- a/g4f/Provider/PerplexityAi.py +++ b/g4f/Provider/PerplexityAi.py @@ -14,7 +14,7 @@ class WebDriver(): class PerplexityAi(BaseProvider): url = "https://www.perplexity.ai" working = True - supports_gpt_4 = True + supports_gpt_35_turbo = True supports_stream = True @classmethod @@ -30,10 +30,6 @@ def create_completion( display: bool = True, **kwargs ) -> CreateResult: - from selenium.webdriver.common.by import By - from selenium.webdriver.support.ui import WebDriverWait - from selenium.webdriver.support import expected_conditions as EC - if browser: driver = browser else: @@ -42,11 +38,18 @@ def create_completion( else: driver = get_browser("", False, proxy) + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + prompt = format_prompt(messages) driver.get(f"{cls.url}/") wait = WebDriverWait(driver, timeout) + # Page loaded? + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']"))) + script = """ window._message = window._last_message = ""; window._message_finished = false; @@ -75,9 +78,6 @@ def create_completion( """ driver.execute_script(script) - # Page loaded? - wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']"))) - if copilot: try: driver.find_element(By.CSS_SELECTOR, "img[alt='User avatar']") diff --git a/g4f/Provider/needs_auth/Bard.py b/g4f/Provider/needs_auth/Bard.py index 092268f8aec..47c89e209f9 100644 --- a/g4f/Provider/needs_auth/Bard.py +++ b/g4f/Provider/needs_auth/Bard.py @@ -1,90 +1,96 @@ from __future__ import annotations -import json -import random -import re +import time +try: + from selenium.webdriver.remote.webdriver import WebDriver +except ImportError: + class WebDriver(): + pass -from aiohttp import ClientSession +from ...typing import CreateResult, Messages +from ..base_provider import BaseProvider +from ..helper import format_prompt, get_browser -from ...typing import Messages -from ..base_provider import AsyncProvider -from ..helper import format_prompt, get_cookies - - -class Bard(AsyncProvider): +class Bard(BaseProvider): url = "https://bard.google.com" - needs_auth = True working = True - _snlm0e = None + needs_auth = True @classmethod - async def create_async( + def create_completion( cls, model: str, messages: Messages, + stream: bool, proxy: str = None, - cookies: dict = None, + browser: WebDriver = None, + display: bool = True, **kwargs - ) -> str: + ) -> CreateResult: prompt = format_prompt(messages) - if not cookies: - cookies = get_cookies(".google.com") + if browser: + driver = browser + else: + if display: + driver, display = get_browser(None, True, proxy) + else: + driver = get_browser(None, False, proxy) - headers = { - 'authority': 'bard.google.com', - 'origin': cls.url, - 'referer': f'{cls.url}/', - 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36', - 'x-same-domain': '1', - } - async with ClientSession( - cookies=cookies, - headers=headers - ) as session: - if not cls._snlm0e: - async with session.get(cls.url, proxy=proxy) as response: - text = await response.text() + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC - if match := re.search(r'SNlM0e\":\"(.*?)\"', text): - cls._snlm0e = match.group(1) + try: + driver.get(f"{cls.url}/chat") + wait = WebDriverWait(driver, 10) + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea"))) + except: + # Reopen browser for login + if not browser: + driver.quit() + # New browser should be visible + if display: + display.stop() + driver = get_browser(None, False, proxy) + driver.get(f"{cls.url}/chat") + wait = WebDriverWait(driver, 240) + wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea"))) + else: + raise RuntimeError("Prompt textarea not found. You may not be logged in.") - else: - raise RuntimeError("No snlm0e value.") - params = { - 'bl': 'boq_assistant-bard-web-server_20230326.21_p0', - '_reqid': random.randint(1111, 9999), - 'rt': 'c' - } - - data = { - 'at': cls._snlm0e, - 'f.req': json.dumps([None, json.dumps([[prompt]])]) - } + try: + # Add hook in XMLHttpRequest + script = """ +const _http_request_open = XMLHttpRequest.prototype.open; +window._message = ""; +XMLHttpRequest.prototype.open = function(method, url) { + if (url.includes("/assistant.lamda.BardFrontendService/StreamGenerate")) { + this.addEventListener("load", (event) => { + window._message = JSON.parse(JSON.parse(this.responseText.split("\\n")[3])[0][2])[4][0][1][0]; + }); + } + return _http_request_open.call(this, method, url); +} +""" + driver.execute_script(script) - intents = '.'.join([ - 'assistant', - 'lamda', - 'BardFrontendService' - ]) - async with session.post( - f'{cls.url}/_/BardChatUi/data/{intents}/StreamGenerate', - data=data, - params=params, - proxy=proxy - ) as response: - response = await response.text() - response = json.loads(response.splitlines()[3])[0][2] - response = json.loads(response)[4][0][1][0] - return response + # Send prompt + driver.find_element(By.CSS_SELECTOR, "div.ql-editor.ql-blank.textarea").send_keys(prompt) + driver.find_element(By.CSS_SELECTOR, "button.send-button").click() - @classmethod - @property - def params(cls): - params = [ - ("model", "str"), - ("messages", "list[dict[str, str]]"), - ("stream", "bool"), - ("proxy", "str"), - ] - param = ", ".join([": ".join(p) for p in params]) - return f"g4f.provider.{cls.__name__} supports: ({param})" + # Read response + script = "return window._message;" + while True: + chunk = driver.execute_script(script) + if chunk: + yield chunk + return + else: + time.sleep(0.1) + finally: + driver.close() + if not browser: + time.sleep(0.1) + driver.quit() + if display: + display.stop() \ No newline at end of file diff --git a/g4f/models.py b/g4f/models.py index 868bf5b274a..5082287c613 100644 --- a/g4f/models.py +++ b/g4f/models.py @@ -5,7 +5,6 @@ from .Provider import ( Chatgpt4Online, ChatAnywhere, - PerplexityAi, GptForLove, ChatgptAi, DeepInfra, @@ -77,7 +76,7 @@ def __all__() -> list[str]: name = 'gpt-4', base_provider = 'openai', best_provider = RetryProvider([ - Bing, Phind, PerplexityAi + Bing, Phind ]) ) From 293337db98240966d815da03402ff7a8c6266a2a Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:29:07 +0100 Subject: [PATCH 05/33] Update helper.py --- g4f/Provider/helper.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/g4f/Provider/helper.py b/g4f/Provider/helper.py index cd9a971cca1..cbbbd75ec66 100644 --- a/g4f/Provider/helper.py +++ b/g4f/Provider/helper.py @@ -17,12 +17,17 @@ firefox, BrowserCookieError ) +try: + from selenium.webdriver.remote.webdriver import WebDriver + except ImportError: + class WebDriver(): + pass try: from undetected_chromedriver import Chrome, ChromeOptions except ImportError: class Chrome(): def __init__(): - raise RuntimeError('Please install "undetected_chromedriver" and "pyvirtualdisplay" package') + raise RuntimeError('Please install the "undetected_chromedriver" package') class ChromeOptions(): def add_argument(): pass From 840157abaa3ef2295c2cd59223802d14bf1527c3 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:31:39 +0100 Subject: [PATCH 06/33] Update helper.py --- g4f/Provider/helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/g4f/Provider/helper.py b/g4f/Provider/helper.py index cbbbd75ec66..dee27fc4c71 100644 --- a/g4f/Provider/helper.py +++ b/g4f/Provider/helper.py @@ -42,6 +42,7 @@ def stop(): from ..typing import Dict, Messages, Union, Tuple from .. import debug +DisplayType = Union[Display, bool] # Change event loop policy on windows if sys.platform == 'win32': From 51a666921093f395778621777152cb829d895ea2 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:35:22 +0100 Subject: [PATCH 07/33] Update Phind.py --- g4f/Provider/Phind.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/g4f/Provider/Phind.py b/g4f/Provider/Phind.py index 56100d87cf1..1499fcb2a8f 100644 --- a/g4f/Provider/Phind.py +++ b/g4f/Provider/Phind.py @@ -2,15 +2,10 @@ import time from urllib.parse import quote -try: - from selenium.webdriver.remote.webdriver import WebDriver -except ImportError: - class WebDriver(): - pass from ..typing import CreateResult, Messages from .base_provider import BaseProvider -from .helper import format_prompt, get_browser +from .helper import WebDriver, format_prompt, get_browser class Phind(BaseProvider): url = "https://www.phind.com" @@ -28,7 +23,7 @@ def create_completion( timeout: int = 120, browser: WebDriver = None, creative_mode: bool = None, - display: bool = True, + hidden_display: bool = True, **kwargs ) -> CreateResult: from selenium.webdriver.common.by import By @@ -38,7 +33,7 @@ def create_completion( if browser: driver = browser else: - if display: + if hidden_display: driver, display = get_browser("", True, proxy) else: driver = get_browser("", False, proxy) @@ -112,5 +107,5 @@ def create_completion( if not browser: time.sleep(0.1) driver.quit() - if display: + if hidden_display: display.stop() \ No newline at end of file From cbc08d6014efe38cf237f893e0945701e81321fb Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:39:34 +0100 Subject: [PATCH 08/33] Update helper.py --- g4f/Provider/helper.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/g4f/Provider/helper.py b/g4f/Provider/helper.py index dee27fc4c71..cad32f053e1 100644 --- a/g4f/Provider/helper.py +++ b/g4f/Provider/helper.py @@ -42,7 +42,6 @@ def stop(): from ..typing import Dict, Messages, Union, Tuple from .. import debug -DisplayType = Union[Display, bool] # Change event loop policy on windows if sys.platform == 'win32': @@ -140,23 +139,24 @@ def format_prompt(messages: Messages, add_special_tokens=False) -> str: def get_browser( user_data_dir: str = None, - display: bool = False, - proxy: str = None + hidden_display: bool = False, + proxy: str = None, + options: ChromeOptions = None ) -> Union[Chrome, Tuple[Chrome, Display]] : if user_data_dir == None: user_data_dir = user_config_dir("g4f") - if display: + if hidden_display: display = Display(visible=0, size=(1920, 1080)) display.start() - options = None if proxy: - options = ChromeOptions() - options.add_argument(f'--proxy-server={proxy}') + if not options: + options = ChromeOptions() + options.add_argument(f'--proxy-server={proxy}') browser = Chrome(user_data_dir=user_data_dir, options=options) - if display: + if hidden_display: return browser, display return browser \ No newline at end of file From 0625bd0a063bf1eee214dd0b0cd6bf2b68068858 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:47:20 +0100 Subject: [PATCH 09/33] Update PerplexityAi.py --- g4f/Provider/PerplexityAi.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/g4f/Provider/PerplexityAi.py b/g4f/Provider/PerplexityAi.py index ebb004060c1..b9de429e29a 100644 --- a/g4f/Provider/PerplexityAi.py +++ b/g4f/Provider/PerplexityAi.py @@ -1,15 +1,10 @@ from __future__ import annotations import time -try: - from selenium.webdriver.remote.webdriver import WebDriver -except ImportError: - class WebDriver(): - pass from ..typing import CreateResult, Messages from .base_provider import BaseProvider -from .helper import format_prompt, get_browser +from .helper import WebDriver, format_prompt, get_browser class PerplexityAi(BaseProvider): url = "https://www.perplexity.ai" @@ -27,13 +22,13 @@ def create_completion( timeout: int = 120, browser: WebDriver = None, copilot: bool = False, - display: bool = True, + hidden_display: bool = True, **kwargs ) -> CreateResult: if browser: driver = browser else: - if display: + if hidden_display: driver, display = get_browser("", True, proxy) else: driver = get_browser("", False, proxy) @@ -50,6 +45,7 @@ def create_completion( # Page loaded? wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']"))) + # Add WebSocket hook script = """ window._message = window._last_message = ""; window._message_finished = false; @@ -80,10 +76,12 @@ def create_completion( if copilot: try: + # Check account driver.find_element(By.CSS_SELECTOR, "img[alt='User avatar']") + # Enable copilot driver.find_element(By.CSS_SELECTOR, "button[data-testid='copilot-toggle']").click() except: - pass + raise RuntimeError("For copilot you needs a account") # Enter question driver.find_element(By.CSS_SELECTOR, "textarea[placeholder='Ask anything...']").send_keys(prompt) @@ -91,6 +89,7 @@ def create_completion( driver.find_element(By.CSS_SELECTOR, "button.bg-super svg[data-icon='arrow-right']").click() try: + # Yield response script = """ if(window._message && window._message != window._last_message) { try { @@ -117,5 +116,5 @@ def create_completion( if not browser: time.sleep(0.1) driver.quit() - if display: + if hidden_display: display.stop() \ No newline at end of file From 5c5235671e734441b80734a66f653844d8758364 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:51:24 +0100 Subject: [PATCH 10/33] Update MyShell.py --- g4f/Provider/MyShell.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/g4f/Provider/MyShell.py b/g4f/Provider/MyShell.py index 09f6feb98bc..dc3934fba38 100644 --- a/g4f/Provider/MyShell.py +++ b/g4f/Provider/MyShell.py @@ -2,15 +2,9 @@ import time, json -try: - from selenium.webdriver.remote.webdriver import WebDriver -except ImportError: - class WebDriver(): - pass - from ..typing import CreateResult, Messages from .base_provider import BaseProvider -from .helper import format_prompt, get_browser +from .helper import WebDriver, format_prompt, get_browser class MyShell(BaseProvider): url = "https://app.myshell.ai/chat" @@ -27,11 +21,11 @@ def create_completion( proxy: str = None, timeout: int = 120, browser: WebDriver = None, - display: bool = True, + hidden_display: bool = True, **kwargs ) -> CreateResult: if not browser: - if display: + if hidden_display: driver, display = get_browser("", True, proxy) else: display = get_browser("", False, proxy) @@ -44,9 +38,11 @@ def create_completion( driver.get(cls.url) try: + # Wait for page load WebDriverWait(driver, timeout).until( EC.presence_of_element_located((By.CSS_SELECTOR, "body:not(.no-js)")) ) + # Send message script = """ response = await fetch("https://api.myshell.ai/v1/bot/chat/send_message", { "headers": { @@ -90,5 +86,5 @@ def create_completion( if not browser: time.sleep(0.1) driver.quit() - if display: + if hidden_display: display.stop() \ No newline at end of file From aa12716974f9ff4eeaed2c012e39e9a06c2f311c Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 18:54:59 +0100 Subject: [PATCH 11/33] Update MyShell.py --- g4f/Provider/MyShell.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/g4f/Provider/MyShell.py b/g4f/Provider/MyShell.py index dc3934fba38..ac3290b5398 100644 --- a/g4f/Provider/MyShell.py +++ b/g4f/Provider/MyShell.py @@ -65,6 +65,7 @@ def create_completion( driver.execute_script(script.replace("{body}", json.dumps(data))) script = """ chunk = await window.reader.read(); +if (chunk['done']) return null; text = await (new Response(chunk['value']).text()); content = ''; text.split('\\n').forEach((line, index) => { @@ -79,8 +80,12 @@ def create_completion( }); return content; """ - while chunk := driver.execute_script(script): - yield chunk + while True: + chunk = driver.execute_script(script): + if chunk: + yield chunk + elif chunk != "": + break finally: driver.close() if not browser: From e0541c513f389cdb1cae97c8d583db529609f96e Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 19:02:53 +0100 Subject: [PATCH 12/33] Update Phind.py --- g4f/Provider/Phind.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/g4f/Provider/Phind.py b/g4f/Provider/Phind.py index 1499fcb2a8f..7fc935af33f 100644 --- a/g4f/Provider/Phind.py +++ b/g4f/Provider/Phind.py @@ -26,10 +26,6 @@ def create_completion( hidden_display: bool = True, **kwargs ) -> CreateResult: - from selenium.webdriver.common.by import By - from selenium.webdriver.support.ui import WebDriverWait - from selenium.webdriver.support import expected_conditions as EC - if browser: driver = browser else: @@ -38,6 +34,10 @@ def create_completion( else: driver = get_browser("", False, proxy) + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + prompt = quote(format_prompt(messages)) driver.get(f"{cls.url}/search?q={prompt}&source=searchbox") @@ -58,6 +58,7 @@ def create_completion( wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".search-container"))) try: + # Fetch hook script = """ window._fetch = window.fetch; window.fetch = (url, options) => { @@ -78,7 +79,7 @@ def create_completion( if(window.reader) { chunk = await window.reader.read(); if (chunk['done']) return null; - text = await (new Response(chunk['value']).text()); + text = (new TextDecoder()).decode(chunk['value']); content = ''; text.split('\\r\\n').forEach((line, index) => { if (line.startsWith('data: ')) { From 79c9051743433fe1953c8b196c1c0480aab07392 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 19:24:15 +0100 Subject: [PATCH 13/33] Update Phind.py --- g4f/Provider/Phind.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/g4f/Provider/Phind.py b/g4f/Provider/Phind.py index 7fc935af33f..2612a92df13 100644 --- a/g4f/Provider/Phind.py +++ b/g4f/Provider/Phind.py @@ -41,39 +41,46 @@ def create_completion( prompt = quote(format_prompt(messages)) driver.get(f"{cls.url}/search?q={prompt}&source=searchbox") + # Need to change settinge if model.startswith("gpt-4") or creative_mode: wait = WebDriverWait(driver, timeout) - # Open dropdown + # Open settings dropdown wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "button.text-dark.dropdown-toggle"))) driver.find_element(By.CSS_SELECTOR, "button.text-dark.dropdown-toggle").click() - # Enable GPT-4 + # Wait for dropdown toggle wait.until(EC.visibility_of_element_located((By.XPATH, "//button[text()='GPT-4']"))) + # Enable GPT-4 if model.startswith("gpt-4"): driver.find_element(By.XPATH, "//button[text()='GPT-4']").click() # Enable creative mode if creative_mode or creative_mode == None: driver.find_element(By.ID, "Creative Mode").click() - # Submit question + # Submit changes driver.find_element(By.CSS_SELECTOR, ".search-bar-input-group button[type='submit']").click() + # Wait for page reload wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".search-container"))) try: - # Fetch hook + # Add fetch hook script = """ window._fetch = window.fetch; window.fetch = (url, options) => { + // Call parent fetch method const result = window._fetch(url, options); if (url != "/api/infer/answer") return result; + // Load response reader result.then((response) => { if (!response.body.locked) { window.reader = response.body.getReader(); } }); + // Return dummy response return new Promise((resolve, reject) => { resolve(new Response(new ReadableStream())) }); } """ + # Read response from reader driver.execute_script(script) script = """ if(window.reader) { From 29df5a21a8827de41f63217d5e3eb102fb80310e Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 19:28:59 +0100 Subject: [PATCH 14/33] Update Bard.py --- g4f/Provider/needs_auth/Bard.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/g4f/Provider/needs_auth/Bard.py b/g4f/Provider/needs_auth/Bard.py index 47c89e209f9..6cb40c90f2a 100644 --- a/g4f/Provider/needs_auth/Bard.py +++ b/g4f/Provider/needs_auth/Bard.py @@ -1,15 +1,10 @@ from __future__ import annotations import time -try: - from selenium.webdriver.remote.webdriver import WebDriver -except ImportError: - class WebDriver(): - pass from ...typing import CreateResult, Messages from ..base_provider import BaseProvider -from ..helper import format_prompt, get_browser +from ..helper import WebDriver, format_prompt, get_browser class Bard(BaseProvider): url = "https://bard.google.com" @@ -24,14 +19,14 @@ def create_completion( stream: bool, proxy: str = None, browser: WebDriver = None, - display: bool = True, + hidden_display: bool = True, **kwargs ) -> CreateResult: prompt = format_prompt(messages) if browser: driver = browser else: - if display: + if hidden_display: driver, display = get_browser(None, True, proxy) else: driver = get_browser(None, False, proxy) @@ -49,7 +44,7 @@ def create_completion( if not browser: driver.quit() # New browser should be visible - if display: + if hidden_display: display.stop() driver = get_browser(None, False, proxy) driver.get(f"{cls.url}/chat") @@ -74,11 +69,11 @@ def create_completion( """ driver.execute_script(script) - # Send prompt + # Input and submit prompt driver.find_element(By.CSS_SELECTOR, "div.ql-editor.ql-blank.textarea").send_keys(prompt) driver.find_element(By.CSS_SELECTOR, "button.send-button").click() - # Read response + # Yield response script = "return window._message;" while True: chunk = driver.execute_script(script) @@ -92,5 +87,5 @@ def create_completion( if not browser: time.sleep(0.1) driver.quit() - if display: + if hidden_display: display.stop() \ No newline at end of file From 56db2051118073d19b0146b23f6e76214f2853a8 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 19:41:31 +0100 Subject: [PATCH 15/33] Update MyShell.py --- g4f/Provider/MyShell.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/g4f/Provider/MyShell.py b/g4f/Provider/MyShell.py index ac3290b5398..70fd35091da 100644 --- a/g4f/Provider/MyShell.py +++ b/g4f/Provider/MyShell.py @@ -24,13 +24,13 @@ def create_completion( hidden_display: bool = True, **kwargs ) -> CreateResult: - if not browser: + if browser: + driver = browser + else: if hidden_display: driver, display = get_browser("", True, proxy) else: - display = get_browser("", False, proxy) - else: - driver = browser + driver = get_browser("", False, proxy) from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait @@ -66,7 +66,7 @@ def create_completion( script = """ chunk = await window.reader.read(); if (chunk['done']) return null; -text = await (new Response(chunk['value']).text()); +text = (new TextDecoder ()).decode(chunk['value']); content = ''; text.split('\\n').forEach((line, index) => { if (line.startsWith('data: ')) { From 235dabf2cc56bc24dddc2fd4220d575f383796f7 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Thu, 16 Nov 2023 19:46:25 +0100 Subject: [PATCH 16/33] Update Liaobots.py --- g4f/Provider/Liaobots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g4f/Provider/Liaobots.py b/g4f/Provider/Liaobots.py index b56d08ab054..4acfcdac84b 100644 --- a/g4f/Provider/Liaobots.py +++ b/g4f/Provider/Liaobots.py @@ -78,7 +78,7 @@ async def create_async_generator( "model": models[model], "messages": messages, "key": "", - "prompt": "You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully.", + "prompt": kwargs.get("system_message", "You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully."), } async with session.post( "https://liaobots.work/api/chat", From c7e047b34c329672769d8e576f4cc51ede1d7bdf Mon Sep 17 00:00:00 2001 From: abc <98614666+xtekky@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:48:40 +0000 Subject: [PATCH 17/33] ~ --- README.md | 2 +- g4f/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 058a605b98e..c1267da66ad 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > By using this repository or any code related to it, you agree to the [legal notice](LEGAL_NOTICE.md). The author is not responsible for any copies, forks, re-uploads made by other users, or anything else related to GPT4Free. This is the author's only account and repository. To prevent impersonation or irresponsible actions, please comply with the GNU GPL license this Repository uses. > [!Note] -> Latest pypi version: [`0.1.8.2`](https://pypi.org/project/g4f/0.1.8.2) +> Latest pypi version: [`0.1.8.3`](https://pypi.org/project/g4f/0.1.8.3) ```sh pip install -U g4f ``` diff --git a/g4f/__init__.py b/g4f/__init__.py index fd8aa30630d..85f5ad95196 100644 --- a/g4f/__init__.py +++ b/g4f/__init__.py @@ -5,7 +5,7 @@ from .typing import Messages, CreateResult, Union, List from . import debug -version = '0.1.8.2' +version = '0.1.8.3' version_check = True def check_pypi_version() -> None: diff --git a/setup.py b/setup.py index 0c9433424d1..0340536f8b4 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ with open('requirements.txt') as f: required = f.read().splitlines() -VERSION = '0.1.8.2' +VERSION = '0.1.8.3' DESCRIPTION = ( 'The official gpt4free repository | various collection of powerful language models' ) From 14f4b0d13ee9538c152187264bf3d97791c5b4f0 Mon Sep 17 00:00:00 2001 From: abc <98614666+xtekky@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:49:15 +0000 Subject: [PATCH 18/33] ~ | g4f `v-0.1.8.4` --- README.md | 2 +- g4f/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1267da66ad..7f721d95585 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > By using this repository or any code related to it, you agree to the [legal notice](LEGAL_NOTICE.md). The author is not responsible for any copies, forks, re-uploads made by other users, or anything else related to GPT4Free. This is the author's only account and repository. To prevent impersonation or irresponsible actions, please comply with the GNU GPL license this Repository uses. > [!Note] -> Latest pypi version: [`0.1.8.3`](https://pypi.org/project/g4f/0.1.8.3) +> Latest pypi version: [`0.1.8.4`](https://pypi.org/project/g4f/0.1.8.4) ```sh pip install -U g4f ``` diff --git a/g4f/__init__.py b/g4f/__init__.py index 85f5ad95196..b5c36a39abf 100644 --- a/g4f/__init__.py +++ b/g4f/__init__.py @@ -5,7 +5,7 @@ from .typing import Messages, CreateResult, Union, List from . import debug -version = '0.1.8.3' +version = '0.1.8.4' version_check = True def check_pypi_version() -> None: diff --git a/setup.py b/setup.py index 0340536f8b4..824e5e729d0 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ with open('requirements.txt') as f: required = f.read().splitlines() -VERSION = '0.1.8.3' +VERSION = '0.1.8.4' DESCRIPTION = ( 'The official gpt4free repository | various collection of powerful language models' ) From 92908b4347ad8a8b231feeaf70cc163bbd85eb29 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Fri, 17 Nov 2023 03:19:27 +0100 Subject: [PATCH 19/33] Add Poe Provider, Update AItianhuSpace Porvider --- g4f/Provider/AItianhuSpace.py | 185 ++++++++++++++++------------ g4f/Provider/MyShell.py | 8 +- g4f/Provider/helper.py | 10 +- g4f/Provider/needs_auth/Poe.py | 129 +++++++++++++++++++ g4f/Provider/needs_auth/__init__.py | 3 +- 5 files changed, 249 insertions(+), 86 deletions(-) create mode 100644 g4f/Provider/needs_auth/Poe.py diff --git a/g4f/Provider/AItianhuSpace.py b/g4f/Provider/AItianhuSpace.py index d316fc6fd0d..a9a824cf5bf 100644 --- a/g4f/Provider/AItianhuSpace.py +++ b/g4f/Provider/AItianhuSpace.py @@ -1,95 +1,128 @@ from __future__ import annotations -import random, json -from .. import debug -from ..typing import AsyncResult, Messages -from ..requests import StreamSession -from .base_provider import AsyncGeneratorProvider, format_prompt, get_cookies +import time +import random -domains = { - "gpt-3.5-turbo": "aitianhu.space", - "gpt-4": "aitianhu.website", -} +from ..typing import CreateResult, Messages +from .base_provider import BaseProvider +from .helper import WebDriver, format_prompt, get_browser +from .. import debug -class AItianhuSpace(AsyncGeneratorProvider): +class AItianhuSpace(BaseProvider): url = "https://chat3.aiyunos.top/" working = True supports_gpt_35_turbo = True + _domains = ["aitianhu.com", "aitianhu1.top"] @classmethod - async def create_async_generator(cls, - model: str, - messages: Messages, - proxy: str = None, - domain: str = None, - cookies: dict = None, - timeout: int = 10, **kwargs) -> AsyncResult: - + def create_completion( + cls, + model: str, + messages: Messages, + stream: bool, + domain: str = None, + proxy: str = None, + timeout: int = 120, + browser: WebDriver = None, + hidden_display: bool = True, + **kwargs + ) -> CreateResult: if not model: model = "gpt-3.5-turbo" - - elif model not in domains: - raise ValueError(f"Model are not supported: {model}") - if not domain: chars = 'abcdefghijklmnopqrstuvwxyz0123456789' rand = ''.join(random.choice(chars) for _ in range(6)) - domain = f"{rand}.{domains[model]}" - + domain = random.choice(cls._domains) + domain = f"{rand}.{domain}" if debug.logging: print(f"AItianhuSpace | using domain: {domain}") + url = f"https://{domain}" + prompt = format_prompt(messages) + if browser: + driver = browser + else: + if hidden_display: + driver, display = get_browser("", True, proxy) + else: + driver = get_browser("", False, proxy) - if not cookies: - cookies = get_cookies('.aitianhu.space') - if not cookies: - raise RuntimeError(f"g4f.provider.{cls.__name__} requires cookies [refresh https://{domain} on chrome]") + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC - url = f'https://{domain}' - async with StreamSession(proxies={"https": proxy}, - cookies=cookies, timeout=timeout, impersonate="chrome110", verify=False) as session: - - data = { - "prompt": format_prompt(messages), - "options": {}, - "systemMessage": "You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully.", - "temperature": 0.8, - "top_p": 1, - **kwargs - } - headers = { - "Authority": url, - "Accept": "application/json, text/plain, */*", - "Origin": url, - "Referer": f"{url}/" - } - async with session.post(f"{url}/api/chat-process", json=data, headers=headers) as response: - response.raise_for_status() - async for line in response.iter_lines(): - if line == b"