diff options
Diffstat (limited to '')
-rw-r--r-- | g4f/Provider/Bing.py | 230 |
1 files changed, 126 insertions, 104 deletions
diff --git a/g4f/Provider/Bing.py b/g4f/Provider/Bing.py index 50e29d23..34687866 100644 --- a/g4f/Provider/Bing.py +++ b/g4f/Provider/Bing.py @@ -15,12 +15,18 @@ from .bing.upload_image import upload_image from .bing.create_images import create_images from .bing.conversation import Conversation, create_conversation, delete_conversation -class Tones(): +class Tones: + """ + Defines the different tone options for the Bing provider. + """ creative = "Creative" balanced = "Balanced" precise = "Precise" class Bing(AsyncGeneratorProvider): + """ + Bing provider for generating responses using the Bing API. + """ url = "https://bing.com/chat" working = True supports_message_history = True @@ -38,6 +44,19 @@ class Bing(AsyncGeneratorProvider): web_search: bool = False, **kwargs ) -> AsyncResult: + """ + Creates an asynchronous generator for producing responses from Bing. + + :param model: The model to use. + :param messages: Messages to process. + :param proxy: Proxy to use for requests. + :param timeout: Timeout for requests. + :param cookies: Cookies for the session. + :param tone: The tone of the response. + :param image: The image type to be used. + :param web_search: Flag to enable or disable web search. + :return: An asynchronous result object. + """ if len(messages) < 2: prompt = messages[0]["content"] context = None @@ -56,65 +75,48 @@ class Bing(AsyncGeneratorProvider): return stream_generate(prompt, tone, image, context, proxy, cookies, web_search, gpt4_turbo, timeout) -def create_context(messages: Messages): +def create_context(messages: Messages) -> str: + """ + Creates a context string from a list of messages. + + :param messages: A list of message dictionaries. + :return: A string representing the context created from the messages. + """ return "".join( - f"[{message['role']}]" + ("(#message)" if message['role']!="system" else "(#additional_instructions)") + f"\n{message['content']}\n\n" + f"[{message['role']}]" + ("(#message)" if message['role'] != "system" else "(#additional_instructions)") + f"\n{message['content']}\n\n" for message in messages ) class Defaults: + """ + Default settings and configurations for the Bing provider. + """ delimiter = "\x1e" ip_address = f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}" + # List of allowed message types for Bing responses allowedMessageTypes = [ - "ActionRequest", - "Chat", - "Context", - # "Disengaged", unwanted - "Progress", - # "AdsQuery", unwanted - "SemanticSerp", - "GenerateContentQuery", - "SearchQuery", - # The following message types should not be added so that it does not flood with - # useless messages (such as "Analyzing images" or "Searching the web") while it's retrieving the AI response - # "InternalSearchQuery", - # "InternalSearchResult", - "RenderCardRequest", - # "RenderContentRequest" + "ActionRequest", "Chat", "Context", "Progress", "SemanticSerp", + "GenerateContentQuery", "SearchQuery", "RenderCardRequest" ] sliceIds = [ - 'abv2', - 'srdicton', - 'convcssclick', - 'stylewv2', - 'contctxp2tf', - '802fluxv1pc_a', - '806log2sphs0', - '727savemem', - '277teditgnds0', - '207hlthgrds0', + 'abv2', 'srdicton', 'convcssclick', 'stylewv2', 'contctxp2tf', + '802fluxv1pc_a', '806log2sphs0', '727savemem', '277teditgnds0', '207hlthgrds0' ] + # Default location settings location = { - "locale": "en-US", - "market": "en-US", - "region": "US", - "locationHints": [ - { - "country": "United States", - "state": "California", - "city": "Los Angeles", - "timezoneoffset": 8, - "countryConfidence": 8, - "Center": {"Latitude": 34.0536909, "Longitude": -118.242766}, - "RegionType": 2, - "SourceType": 1, - } - ], + "locale": "en-US", "market": "en-US", "region": "US", + "locationHints": [{ + "country": "United States", "state": "California", "city": "Los Angeles", + "timezoneoffset": 8, "countryConfidence": 8, + "Center": {"Latitude": 34.0536909, "Longitude": -118.242766}, + "RegionType": 2, "SourceType": 1 + }], } + # Default headers for requests headers = { 'accept': '*/*', 'accept-language': 'en-US,en;q=0.9', @@ -139,23 +141,13 @@ class Defaults: } optionsSets = [ - 'nlu_direct_response_filter', - 'deepleo', - 'disable_emoji_spoken_text', - 'responsible_ai_policy_235', - 'enablemm', - 'iyxapbing', - 'iycapbing', - 'gencontentv3', - 'fluxsrtrunc', - 'fluxtrunc', - 'fluxv1', - 'rai278', - 'replaceurl', - 'eredirecturl', - 'nojbfedge' + 'nlu_direct_response_filter', 'deepleo', 'disable_emoji_spoken_text', + 'responsible_ai_policy_235', 'enablemm', 'iyxapbing', 'iycapbing', + 'gencontentv3', 'fluxsrtrunc', 'fluxtrunc', 'fluxv1', 'rai278', + 'replaceurl', 'eredirecturl', 'nojbfedge' ] + # Default cookies cookies = { 'SRCHD' : 'AF=NOFORM', 'PPLState' : '1', @@ -166,6 +158,12 @@ class Defaults: } def format_message(msg: dict) -> str: + """ + Formats a message dictionary into a JSON string with a delimiter. + + :param msg: The message dictionary to format. + :return: A formatted string representation of the message. + """ return json.dumps(msg, ensure_ascii=False) + Defaults.delimiter def create_message( @@ -177,7 +175,20 @@ def create_message( web_search: bool = False, gpt4_turbo: bool = False ) -> str: + """ + Creates a message for the Bing API with specified parameters. + + :param conversation: The current conversation object. + :param prompt: The user's input prompt. + :param tone: The desired tone for the response. + :param context: Additional context for the prompt. + :param image_response: The response if an image is involved. + :param web_search: Flag to enable web search. + :param gpt4_turbo: Flag to enable GPT-4 Turbo. + :return: A formatted string message for the Bing API. + """ options_sets = Defaults.optionsSets + # Append tone-specific options if tone == Tones.creative: options_sets.append("h3imaginative") elif tone == Tones.precise: @@ -186,54 +197,49 @@ def create_message( options_sets.append("galileo") else: options_sets.append("harmonyv3") - + + # Additional configurations based on parameters if not web_search: options_sets.append("nosearchall") - if gpt4_turbo: options_sets.append("dlgpt4t") - + request_id = str(uuid.uuid4()) struct = { - 'arguments': [ - { - 'source': 'cib', - 'optionsSets': options_sets, - 'allowedMessageTypes': Defaults.allowedMessageTypes, - 'sliceIds': Defaults.sliceIds, - 'traceId': os.urandom(16).hex(), - 'isStartOfSession': True, + 'arguments': [{ + 'source': 'cib', 'optionsSets': options_sets, + 'allowedMessageTypes': Defaults.allowedMessageTypes, + 'sliceIds': Defaults.sliceIds, + 'traceId': os.urandom(16).hex(), 'isStartOfSession': True, + 'requestId': request_id, + 'message': { + **Defaults.location, + 'author': 'user', + 'inputMethod': 'Keyboard', + 'text': prompt, + 'messageType': 'Chat', 'requestId': request_id, - 'message': {**Defaults.location, **{ - 'author': 'user', - 'inputMethod': 'Keyboard', - 'text': prompt, - 'messageType': 'Chat', - 'requestId': request_id, - 'messageId': request_id, - }}, - "verbosity": "verbose", - "scenario": "SERP", - "plugins":[ - {"id":"c310c353-b9f0-4d76-ab0d-1dd5e979cf68", "category": 1} - ] if web_search else [], - 'tone': tone, - 'spokenTextMode': 'None', - 'conversationId': conversation.conversationId, - 'participant': { - 'id': conversation.clientId - }, - } - ], + 'messageId': request_id + }, + "verbosity": "verbose", + "scenario": "SERP", + "plugins": [{"id": "c310c353-b9f0-4d76-ab0d-1dd5e979cf68", "category": 1}] if web_search else [], + 'tone': tone, + 'spokenTextMode': 'None', + 'conversationId': conversation.conversationId, + 'participant': {'id': conversation.clientId}, + }], 'invocationId': '1', 'target': 'chat', 'type': 4 } - if image_response.get('imageUrl') and image_response.get('originalImageUrl'): + + if image_response and image_response.get('imageUrl') and image_response.get('originalImageUrl'): struct['arguments'][0]['message']['originalImageUrl'] = image_response.get('originalImageUrl') struct['arguments'][0]['message']['imageUrl'] = image_response.get('imageUrl') struct['arguments'][0]['experienceType'] = None struct['arguments'][0]['attachedFileInfo'] = {"fileName": None, "fileType": None} + if context: struct['arguments'][0]['previousMessages'] = [{ "author": "user", @@ -242,30 +248,46 @@ def create_message( "messageType": "Context", "messageId": "discover-web--page-ping-mriduna-----" }] + return format_message(struct) async def stream_generate( - prompt: str, - tone: str, - image: ImageType = None, - context: str = None, - proxy: str = None, - cookies: dict = None, - web_search: bool = False, - gpt4_turbo: bool = False, - timeout: int = 900 - ): + prompt: str, + tone: str, + image: ImageType = None, + context: str = None, + proxy: str = None, + cookies: dict = None, + web_search: bool = False, + gpt4_turbo: bool = False, + timeout: int = 900 +): + """ + Asynchronously streams generated responses from the Bing API. + + :param prompt: The user's input prompt. + :param tone: The desired tone for the response. + :param image: The image type involved in the response. + :param context: Additional context for the prompt. + :param proxy: Proxy settings for the request. + :param cookies: Cookies for the session. + :param web_search: Flag to enable web search. + :param gpt4_turbo: Flag to enable GPT-4 Turbo. + :param timeout: Timeout for the request. + :return: An asynchronous generator yielding responses. + """ headers = Defaults.headers if cookies: headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items()) + async with ClientSession( - timeout=ClientTimeout(total=timeout), - headers=headers + timeout=ClientTimeout(total=timeout), headers=headers ) as session: conversation = await create_conversation(session, proxy) image_response = await upload_image(session, image, tone, proxy) if image else None if image_response: yield image_response + try: async with session.ws_connect( 'wss://sydney.bing.com/sydney/ChatHub', @@ -289,7 +311,7 @@ async def stream_generate( if obj is None or not obj: continue response = json.loads(obj) - if response.get('type') == 1 and response['arguments'][0].get('messages'): + if response and response.get('type') == 1 and response['arguments'][0].get('messages'): message = response['arguments'][0]['messages'][0] image_response = None if (message['contentOrigin'] != 'Apology'): |