From 86e36efe6bbae10286767b44c6a79913e5199de1 Mon Sep 17 00:00:00 2001 From: H Lohaus Date: Sat, 28 Dec 2024 16:50:08 +0100 Subject: Add Path and PathLike support when uploading images (#2514) * Add Path and PathLike support when uploading images Improve raise_for_status in special cases Move ImageResponse to providers.response module Improve OpenaiChat and OpenaiAccount providers Add Sources for web_search in OpenaiChat Add JsonConversation for import and export conversations to js Add RequestLogin response type Add TitleGeneration support in OpenaiChat and gui * Improve Docker Container Guide in README.md * Add tool calls api support, add search tool support --- g4f/Provider/bing/conversation.py | 93 ----------------------- g4f/Provider/bing/upload_image.py | 150 -------------------------------------- 2 files changed, 243 deletions(-) delete mode 100644 g4f/Provider/bing/conversation.py delete mode 100644 g4f/Provider/bing/upload_image.py (limited to 'g4f/Provider/bing') diff --git a/g4f/Provider/bing/conversation.py b/g4f/Provider/bing/conversation.py deleted file mode 100644 index 43bcbb4d..00000000 --- a/g4f/Provider/bing/conversation.py +++ /dev/null @@ -1,93 +0,0 @@ -from __future__ import annotations - -from ...requests import StreamSession, raise_for_status -from ...errors import RateLimitError -from ...providers.response import BaseConversation - -class Conversation(BaseConversation): - """ - Represents a conversation with specific attributes. - """ - def __init__(self, conversationId: str, clientId: str, conversationSignature: str) -> None: - """ - Initialize a new conversation instance. - - Args: - conversationId (str): Unique identifier for the conversation. - clientId (str): Client identifier. - conversationSignature (str): Signature for the conversation. - """ - self.conversationId = conversationId - self.clientId = clientId - self.conversationSignature = conversationSignature - -async def create_conversation(session: StreamSession, headers: dict, tone: str) -> Conversation: - """ - Create a new conversation asynchronously. - - Args: - session (ClientSession): An instance of aiohttp's ClientSession. - proxy (str, optional): Proxy URL. Defaults to None. - - Returns: - Conversation: An instance representing the created conversation. - """ - if tone == "Copilot": - url = "https://copilot.microsoft.com/turing/conversation/create?bundleVersion=1.1809.0" - else: - url = "https://www.bing.com/turing/conversation/create?bundleVersion=1.1809.0" - async with session.get(url, headers=headers) as response: - if response.status == 404: - raise RateLimitError("Response 404: Do less requests and reuse conversations") - await raise_for_status(response, "Failed to create conversation") - data = await response.json() - if not data: - raise RuntimeError('Empty response: Failed to create conversation') - conversationId = data.get('conversationId') - clientId = data.get('clientId') - conversationSignature = response.headers.get('X-Sydney-Encryptedconversationsignature') - if not conversationId or not clientId or not conversationSignature: - raise RuntimeError('Empty fields: Failed to create conversation') - return Conversation(conversationId, clientId, conversationSignature) - -async def list_conversations(session: StreamSession) -> list: - """ - List all conversations asynchronously. - - Args: - session (ClientSession): An instance of aiohttp's ClientSession. - - Returns: - list: A list of conversations. - """ - url = "https://www.bing.com/turing/conversation/chats" - async with session.get(url) as response: - response = await response.json() - return response["chats"] - -async def delete_conversation(session: StreamSession, conversation: Conversation, headers: dict) -> bool: - """ - Delete a conversation asynchronously. - - Args: - session (ClientSession): An instance of aiohttp's ClientSession. - conversation (Conversation): The conversation to delete. - proxy (str, optional): Proxy URL. Defaults to None. - - Returns: - bool: True if deletion was successful, False otherwise. - """ - url = "https://sydney.bing.com/sydney/DeleteSingleConversation" - json = { - "conversationId": conversation.conversationId, - "conversationSignature": conversation.conversationSignature, - "participant": {"id": conversation.clientId}, - "source": "cib", - "optionsSets": ["autosave"] - } - try: - async with session.post(url, json=json, headers=headers) as response: - response = await response.json() - return response["result"]["value"] == "Success" - except: - return False diff --git a/g4f/Provider/bing/upload_image.py b/g4f/Provider/bing/upload_image.py deleted file mode 100644 index c517e493..00000000 --- a/g4f/Provider/bing/upload_image.py +++ /dev/null @@ -1,150 +0,0 @@ -""" -Module to handle image uploading and processing for Bing AI integrations. -""" -from __future__ import annotations - -import json -import math -from aiohttp import ClientSession, FormData - -from ...typing import ImageType, Tuple -from ...image import to_image, process_image, to_base64_jpg, ImageRequest, Image -from ...requests import raise_for_status - -IMAGE_CONFIG = { - "maxImagePixels": 360000, - "imageCompressionRate": 0.7, - "enableFaceBlurDebug": False, -} - -async def upload_image( - session: ClientSession, - image_data: ImageType, - tone: str, - headers: dict -) -> ImageRequest: - """ - Uploads an image to Bing's AI service and returns the image response. - - Args: - session (ClientSession): The active session. - image_data (bytes): The image data to be uploaded. - tone (str): The tone of the conversation. - proxy (str, optional): Proxy if any. Defaults to None. - - Raises: - RuntimeError: If the image upload fails. - - Returns: - ImageRequest: The response from the image upload. - """ - image = to_image(image_data) - new_width, new_height = calculate_new_dimensions(image) - image = process_image(image, new_width, new_height) - img_binary_data = to_base64_jpg(image, IMAGE_CONFIG['imageCompressionRate']) - - data = build_image_upload_payload(img_binary_data, tone) - - async with session.post("https://www.bing.com/images/kblob", data=data, headers=prepare_headers(headers)) as response: - await raise_for_status(response, "Failed to upload image") - return parse_image_response(await response.json()) - -def calculate_new_dimensions(image: Image) -> Tuple[int, int]: - """ - Calculates the new dimensions for the image based on the maximum allowed pixels. - - Args: - image (Image): The PIL Image object. - - Returns: - Tuple[int, int]: The new width and height for the image. - """ - width, height = image.size - max_image_pixels = IMAGE_CONFIG['maxImagePixels'] - if max_image_pixels / (width * height) < 1: - scale_factor = math.sqrt(max_image_pixels / (width * height)) - return int(width * scale_factor), int(height * scale_factor) - return width, height - -def build_image_upload_payload(image_bin: str, tone: str) -> FormData: - """ - Builds the payload for image uploading. - - Args: - image_bin (str): Base64 encoded image binary data. - tone (str): The tone of the conversation. - - Returns: - Tuple[str, str]: The data and boundary for the payload. - """ - data = FormData() - knowledge_request = json.dumps(build_knowledge_request(tone), ensure_ascii=False) - data.add_field('knowledgeRequest', knowledge_request, content_type="application/json") - data.add_field('imageBase64', image_bin) - return data - -def build_knowledge_request(tone: str) -> dict: - """ - Builds the knowledge request payload. - - Args: - tone (str): The tone of the conversation. - - Returns: - dict: The knowledge request payload. - """ - return { - "imageInfo": {}, - "knowledgeRequest": { - 'invokedSkills': ["ImageById"], - 'subscriptionId': "Bing.Chat.Multimodal", - 'invokedSkillsRequestData': { - 'enableFaceBlur': True - }, - 'convoData': { - 'convoid': "", - 'convotone': tone - } - } - } - -def prepare_headers(headers: dict) -> dict: - """ - Prepares the headers for the image upload request. - - Args: - session (ClientSession): The active session. - boundary (str): The boundary string for the multipart/form-data. - - Returns: - dict: The headers for the request. - """ - headers["Referer"] = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx' - headers["Origin"] = 'https://www.bing.com' - return headers - -def parse_image_response(response: dict) -> ImageRequest: - """ - Parses the response from the image upload. - - Args: - response (dict): The response dictionary. - - Raises: - RuntimeError: If parsing the image info fails. - - Returns: - ImageRequest: The parsed image response. - """ - if not response.get('blobId'): - raise RuntimeError("Failed to parse image info.") - - result = {'bcid': response.get('blobId', ""), 'blurredBcid': response.get('processedBlobId', "")} - result["imageUrl"] = f"https://www.bing.com/images/blob?bcid={result['blurredBcid'] or result['bcid']}" - - result['originalImageUrl'] = ( - f"https://www.bing.com/images/blob?bcid={result['blurredBcid']}" - if IMAGE_CONFIG["enableFaceBlurDebug"] else - f"https://www.bing.com/images/blob?bcid={result['bcid']}" - ) - return ImageRequest(result) \ No newline at end of file -- cgit v1.2.3