From aba4b96f23ef38ca75195b3b78a88bb92035b4a9 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Mon, 12 Feb 2024 11:41:27 +0100 Subject: Add new Client API with Docs Use object urls for the preview of image uploads. Fix upload images in You provider Fix create image. It's now a single image. Improve system message for create images. --- g4f/Provider/BingCreateImages.py | 56 +++++++++++++++++++++ g4f/Provider/CreateImagesBing.py | 94 ----------------------------------- g4f/Provider/You.py | 5 ++ g4f/Provider/__init__.py | 2 +- g4f/Provider/bing/create_images.py | 43 ++++++++++++++-- g4f/Provider/create_images.py | 8 ++- g4f/Provider/needs_auth/OpenaiChat.py | 85 +++++++++++++++---------------- 7 files changed, 149 insertions(+), 144 deletions(-) create mode 100644 g4f/Provider/BingCreateImages.py delete mode 100644 g4f/Provider/CreateImagesBing.py (limited to 'g4f/Provider') diff --git a/g4f/Provider/BingCreateImages.py b/g4f/Provider/BingCreateImages.py new file mode 100644 index 00000000..901b7787 --- /dev/null +++ b/g4f/Provider/BingCreateImages.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import asyncio +import os +from typing import Generator + +from ..cookies import get_cookies +from ..image import ImageResponse +from ..errors import MissingRequirementsError, MissingAuthError +from .bing.create_images import create_images, create_session, get_cookies_from_browser + +class BingCreateImages: + """A class for creating images using Bing.""" + + def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None: + self.cookies = cookies + self.proxy = proxy + + def create(self, prompt: str) -> Generator[ImageResponse, None, None]: + """ + Generator for creating imagecompletion based on a prompt. + + Args: + prompt (str): Prompt to generate images. + + Yields: + Generator[str, None, None]: The final output as markdown formatted string with images. + """ + cookies = self.cookies or get_cookies(".bing.com", False) + if "_U" not in cookies: + login_url = os.environ.get("G4F_LOGIN_URL") + if login_url: + yield f"Please login: [Bing]({login_url})\n\n" + try: + self.cookies = get_cookies_from_browser(self.proxy) + except MissingRequirementsError as e: + raise MissingAuthError(f'Missing "_U" cookie. {e}') + yield asyncio.run(self.create_async(prompt)) + + async def create_async(self, prompt: str) -> ImageResponse: + """ + Asynchronously creates a markdown formatted string with images based on the prompt. + + Args: + prompt (str): Prompt to generate images. + + Returns: + str: Markdown formatted string with images. + """ + cookies = self.cookies or get_cookies(".bing.com", False) + if "_U" not in cookies: + raise MissingAuthError('Missing "_U" cookie') + proxy = self.proxy or os.environ.get("G4F_PROXY") + async with create_session(cookies, proxy) as session: + images = await create_images(session, prompt, proxy) + return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"} if len(images) > 1 else {}) \ No newline at end of file diff --git a/g4f/Provider/CreateImagesBing.py b/g4f/Provider/CreateImagesBing.py deleted file mode 100644 index 59eafe0c..00000000 --- a/g4f/Provider/CreateImagesBing.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import annotations - -import asyncio -import time -import os -from typing import Generator - -from ..cookies import get_cookies -from ..webdriver import WebDriver, get_driver_cookies, get_browser -from ..image import ImageResponse -from ..errors import MissingRequirementsError, MissingAuthError -from .bing.create_images import BING_URL, create_images, create_session - -BING_URL = "https://www.bing.com" -TIMEOUT_LOGIN = 1200 - -def wait_for_login(driver: WebDriver, timeout: int = TIMEOUT_LOGIN) -> None: - """ - Waits for the user to log in within a given timeout period. - - Args: - driver (WebDriver): Webdriver for browser automation. - timeout (int): Maximum waiting time in seconds. - - Raises: - RuntimeError: If the login process exceeds the timeout. - """ - driver.get(f"{BING_URL}/") - start_time = time.time() - while not driver.get_cookie("_U"): - if time.time() - start_time > timeout: - raise RuntimeError("Timeout error") - time.sleep(0.5) - -def get_cookies_from_browser(proxy: str = None) -> dict[str, str]: - """ - Retrieves cookies from the browser using webdriver. - - Args: - proxy (str, optional): Proxy configuration. - - Returns: - dict[str, str]: Retrieved cookies. - """ - with get_browser(proxy=proxy) as driver: - wait_for_login(driver) - time.sleep(1) - return get_driver_cookies(driver) - -class CreateImagesBing: - """A class for creating images using Bing.""" - - def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None: - self.cookies = cookies - self.proxy = proxy - - def create_completion(self, prompt: str) -> Generator[ImageResponse, None, None]: - """ - Generator for creating imagecompletion based on a prompt. - - Args: - prompt (str): Prompt to generate images. - - Yields: - Generator[str, None, None]: The final output as markdown formatted string with images. - """ - cookies = self.cookies or get_cookies(".bing.com", False) - if "_U" not in cookies: - login_url = os.environ.get("G4F_LOGIN_URL") - if login_url: - yield f"Please login: [Bing]({login_url})\n\n" - try: - self.cookies = get_cookies_from_browser(self.proxy) - except MissingRequirementsError as e: - raise MissingAuthError(f'Missing "_U" cookie. {e}') - yield asyncio.run(self.create_async(prompt)) - - async def create_async(self, prompt: str) -> ImageResponse: - """ - Asynchronously creates a markdown formatted string with images based on the prompt. - - Args: - prompt (str): Prompt to generate images. - - Returns: - str: Markdown formatted string with images. - """ - cookies = self.cookies or get_cookies(".bing.com", False) - if "_U" not in cookies: - raise MissingAuthError('Missing "_U" cookie') - proxy = self.proxy or os.environ.get("G4F_PROXY") - async with create_session(cookies, proxy) as session: - images = await create_images(session, prompt, proxy) - return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"}) \ No newline at end of file diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py index 001f775d..ece1d340 100644 --- a/g4f/Provider/You.py +++ b/g4f/Provider/You.py @@ -58,9 +58,14 @@ class You(AsyncGeneratorProvider): "selectedChatMode": chat_mode, #"chat": json.dumps(chat), } + params = { + "userFiles": upload, + "selectedChatMode": chat_mode, + } async with (client.post if chat_mode == "default" else client.get)( f"{cls.url}/api/streamingSearch", data=data, + params=params, headers=headers, cookies=cookies ) as response: diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index 7dbc1504..68b62fd9 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -53,7 +53,7 @@ from .Vercel import Vercel from .Ylokh import Ylokh from .You import You -from .CreateImagesBing import CreateImagesBing +from .BingCreateImages import BingCreateImages import sys diff --git a/g4f/Provider/bing/create_images.py b/g4f/Provider/bing/create_images.py index de4fd476..7b82dc56 100644 --- a/g4f/Provider/bing/create_images.py +++ b/g4f/Provider/bing/create_images.py @@ -21,8 +21,10 @@ from ..create_images import CreateImagesProvider from ..helper import get_connector from ...base_provider import ProviderType from ...errors import MissingRequirementsError +from ...webdriver import WebDriver, get_driver_cookies, get_browser BING_URL = "https://www.bing.com" +TIMEOUT_LOGIN = 1200 TIMEOUT_IMAGE_CREATION = 300 ERRORS = [ "this prompt is being reviewed", @@ -35,6 +37,39 @@ BAD_IMAGES = [ "https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg", ] +def wait_for_login(driver: WebDriver, timeout: int = TIMEOUT_LOGIN) -> None: + """ + Waits for the user to log in within a given timeout period. + + Args: + driver (WebDriver): Webdriver for browser automation. + timeout (int): Maximum waiting time in seconds. + + Raises: + RuntimeError: If the login process exceeds the timeout. + """ + driver.get(f"{BING_URL}/") + start_time = time.time() + while not driver.get_cookie("_U"): + if time.time() - start_time > timeout: + raise RuntimeError("Timeout error") + time.sleep(0.5) + +def get_cookies_from_browser(proxy: str = None) -> dict[str, str]: + """ + Retrieves cookies from the browser using webdriver. + + Args: + proxy (str, optional): Proxy configuration. + + Returns: + dict[str, str]: Retrieved cookies. + """ + with get_browser(proxy=proxy) as driver: + wait_for_login(driver) + time.sleep(1) + return get_driver_cookies(driver) + def create_session(cookies: Dict[str, str], proxy: str = None, connector: BaseConnector = None) -> ClientSession: """ Creates a new client session with specified cookies and headers. @@ -141,6 +176,8 @@ def read_images(html_content: str) -> List[str]: """ soup = BeautifulSoup(html_content, "html.parser") tags = soup.find_all("img", class_="mimg") + if not tags: + tags = soup.find_all("img", class_="gir_mmimg") images = [img["src"].split("?w=")[0] for img in tags] if any(im in BAD_IMAGES for im in images): raise RuntimeError("Bad images found") @@ -158,10 +195,10 @@ def patch_provider(provider: ProviderType) -> CreateImagesProvider: Returns: CreateImagesProvider: The patched provider with image creation capabilities. """ - from ..CreateImagesBing import CreateImagesBing - service = CreateImagesBing() + from ..BingCreateImages import BingCreateImages + service = BingCreateImages() return CreateImagesProvider( provider, - service.create_completion, + service.create, service.create_async ) \ No newline at end of file diff --git a/g4f/Provider/create_images.py b/g4f/Provider/create_images.py index 9a9e3f08..2ca92432 100644 --- a/g4f/Provider/create_images.py +++ b/g4f/Provider/create_images.py @@ -7,10 +7,14 @@ from ..typing import CreateResult, Messages from ..base_provider import BaseProvider, ProviderType system_message = """ -You can generate custom images with the DALL-E 3 image generator. +You can generate images, pictures, photos or img with the DALL-E 3 image generator. To generate an image with a prompt, do this: + -Don't use images with data uri. It is important to use a prompt instead. + +Never use own image links. Don't wrap it in backticks. +It is important to use a only a img tag with a prompt. + """ diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py index b1b267fd..9e0edd8a 100644 --- a/g4f/Provider/needs_auth/OpenaiChat.py +++ b/g4f/Provider/needs_auth/OpenaiChat.py @@ -386,50 +386,47 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin): ) as response: if not response.ok: raise RuntimeError(f"Response {response.status_code}: {await response.text()}") - try: - last_message: int = 0 - async for line in response.iter_lines(): - if not line.startswith(b"data: "): - continue - elif line.startswith(b"data: [DONE]"): - break - try: - line = json.loads(line[6:]) - except: - continue - if "message" not in line: - continue - if "error" in line and line["error"]: - raise RuntimeError(line["error"]) - if "message_type" not in line["message"]["metadata"]: - continue - try: - image_response = await cls.get_generated_image(session, auth_headers, line) - if image_response: - yield image_response - except Exception as e: - yield e - if line["message"]["author"]["role"] != "assistant": - continue - if line["message"]["content"]["content_type"] != "text": - continue - if line["message"]["metadata"]["message_type"] not in ("next", "continue", "variant"): - continue - conversation_id = line["conversation_id"] - parent_id = line["message"]["id"] - if response_fields: - response_fields = False - yield ResponseFields(conversation_id, parent_id, end_turn) - if "parts" in line["message"]["content"]: - new_message = line["message"]["content"]["parts"][0] - if len(new_message) > last_message: - yield new_message[last_message:] - last_message = len(new_message) - if "finish_details" in line["message"]["metadata"]: - if line["message"]["metadata"]["finish_details"]["type"] == "stop": - end_turn.end() - except Exception as e: - raise e + last_message: int = 0 + async for line in response.iter_lines(): + if not line.startswith(b"data: "): + continue + elif line.startswith(b"data: [DONE]"): + break + try: + line = json.loads(line[6:]) + except: + continue + if "message" not in line: + continue + if "error" in line and line["error"]: + raise RuntimeError(line["error"]) + if "message_type" not in line["message"]["metadata"]: + continue + try: + image_response = await cls.get_generated_image(session, auth_headers, line) + if image_response: + yield image_response + except Exception as e: + yield e + if line["message"]["author"]["role"] != "assistant": + continue + if line["message"]["content"]["content_type"] != "text": + continue + if line["message"]["metadata"]["message_type"] not in ("next", "continue", "variant"): + continue + conversation_id = line["conversation_id"] + parent_id = line["message"]["id"] + if response_fields: + response_fields = False + yield ResponseFields(conversation_id, parent_id, end_turn) + if "parts" in line["message"]["content"]: + new_message = line["message"]["content"]["parts"][0] + if len(new_message) > last_message: + yield new_message[last_message:] + last_message = len(new_message) + if "finish_details" in line["message"]["metadata"]: + if line["message"]["metadata"]["finish_details"]["type"] == "stop": + end_turn.end() if not auto_continue: break action = "continue" -- cgit v1.2.3