From 9e3c046d5ce629ef8b8537deb56cd8838b5596c6 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Fri, 6 Dec 2024 08:54:49 +0100 Subject: Add conversation support in HuggingChat --- g4f/Provider/MagickPen.py | 2 +- g4f/Provider/Pizzagpt.py | 6 +- g4f/Provider/Prodia.py | 1 - g4f/Provider/Upstage.py | 2 +- g4f/Provider/needs_auth/HuggingChat.py | 104 +++++++++++++++++++----------- g4f/Provider/needs_auth/PollinationsAI.py | 1 + 6 files changed, 71 insertions(+), 45 deletions(-) (limited to 'g4f') diff --git a/g4f/Provider/MagickPen.py b/g4f/Provider/MagickPen.py index 7f1751dd..1d084a2f 100644 --- a/g4f/Provider/MagickPen.py +++ b/g4f/Provider/MagickPen.py @@ -13,7 +13,7 @@ from .helper import format_prompt class MagickPen(AsyncGeneratorProvider, ProviderModelMixin): url = "https://magickpen.com" api_endpoint = "https://api.magickpen.com/ask" - working = True + working = False supports_stream = True supports_system_message = True supports_message_history = True diff --git a/g4f/Provider/Pizzagpt.py b/g4f/Provider/Pizzagpt.py index 6513bd34..16946a1e 100644 --- a/g4f/Provider/Pizzagpt.py +++ b/g4f/Provider/Pizzagpt.py @@ -1,6 +1,5 @@ from __future__ import annotations -import json from aiohttp import ClientSession from ..typing import AsyncResult, Messages @@ -45,5 +44,6 @@ class Pizzagpt(AsyncGeneratorProvider, ProviderModelMixin): async with session.post(f"{cls.url}{cls.api_endpoint}", json=data, proxy=proxy) as response: response.raise_for_status() response_json = await response.json() - content = response_json.get("answer", {}).get("content", "") - yield content + content = response_json.get("answer", response_json).get("content") + if content: + yield content diff --git a/g4f/Provider/Prodia.py b/g4f/Provider/Prodia.py index fcebf7e3..847da6d7 100644 --- a/g4f/Provider/Prodia.py +++ b/g4f/Provider/Prodia.py @@ -1,7 +1,6 @@ from __future__ import annotations from aiohttp import ClientSession -import time import asyncio from ..typing import AsyncResult, Messages diff --git a/g4f/Provider/Upstage.py b/g4f/Provider/Upstage.py index 81234ed9..f6683c45 100644 --- a/g4f/Provider/Upstage.py +++ b/g4f/Provider/Upstage.py @@ -11,7 +11,7 @@ from .helper import format_prompt class Upstage(AsyncGeneratorProvider, ProviderModelMixin): url = "https://console.upstage.ai/playground/chat" api_endpoint = "https://ap-northeast-2.apistage.ai/v1/web/demo/chat/completions" - working = True + working = False default_model = 'solar-pro' models = [ 'upstage/solar-1-mini-chat', diff --git a/g4f/Provider/needs_auth/HuggingChat.py b/g4f/Provider/needs_auth/HuggingChat.py index dec74fe6..fc50e4d8 100644 --- a/g4f/Provider/needs_auth/HuggingChat.py +++ b/g4f/Provider/needs_auth/HuggingChat.py @@ -12,8 +12,14 @@ from ...typing import CreateResult, Messages, Cookies from ...errors import MissingRequirementsError from ...requests.raise_for_status import raise_for_status from ...cookies import get_cookies -from ..base_provider import ProviderModelMixin, AbstractProvider +from ..base_provider import ProviderModelMixin, AbstractProvider, BaseConversation from ..helper import format_prompt +from ... import debug + +class Conversation(BaseConversation): + def __init__(self, conversation_id: str, message_id: str): + self.conversation_id = conversation_id + self.message_id = message_id class HuggingChat(AbstractProvider, ProviderModelMixin): url = "https://huggingface.co/chat" @@ -54,6 +60,8 @@ class HuggingChat(AbstractProvider, ProviderModelMixin): model: str, messages: Messages, stream: bool, + return_conversation: bool = False, + conversation: Conversation = None, web_search: bool = False, cookies: Cookies = None, **kwargs @@ -81,45 +89,23 @@ class HuggingChat(AbstractProvider, ProviderModelMixin): 'sec-fetch-site': 'same-origin', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', } - json_data = { - 'model': model, - } - response = session.post('https://huggingface.co/chat/conversation', json=json_data) - raise_for_status(response) - conversationId = response.json().get('conversationId') - - # Get the data response and parse it properly - response = session.get(f'https://huggingface.co/chat/conversation/{conversationId}/__data.json?x-sveltekit-invalidated=11') - raise_for_status(response) + if conversation is None: + conversationId = cls.create_conversation(session, model) + messageId = cls.fetch_message_id(session, conversationId) + conversation = Conversation(conversationId, messageId) + if return_conversation: + yield conversation + inputs = format_prompt(messages) + else: + conversation.message_id = cls.fetch_message_id(session, conversation.conversation_id) + inputs = messages[-1]["content"] - # Split the response content by newlines and parse each line as JSON - try: - json_data = None - for line in response.text.split('\n'): - if line.strip(): - try: - parsed = json.loads(line) - if isinstance(parsed, dict) and "nodes" in parsed: - json_data = parsed - break - except json.JSONDecodeError: - continue - - if not json_data: - raise RuntimeError("Failed to parse response data") - - data: list = json_data["nodes"][1]["data"] - keys: list[int] = data[data[0]["messages"]] - message_keys: dict = data[keys[0]] - messageId: str = data[message_keys["id"]] - - except (KeyError, IndexError, TypeError) as e: - raise RuntimeError(f"Failed to extract message ID: {str(e)}") + debug.log(f"Use conversation: {conversation.conversation_id} Use message: {conversation.message_id}") settings = { - "inputs": format_prompt(messages), - "id": messageId, + "inputs": inputs, + "id": conversation.message_id, "is_retry": False, "is_continue": False, "web_search": web_search, @@ -133,7 +119,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin): 'origin': 'https://huggingface.co', 'pragma': 'no-cache', 'priority': 'u=1, i', - 'referer': f'https://huggingface.co/chat/conversation/{conversationId}', + 'referer': f'https://huggingface.co/chat/conversation/{conversation.conversation_id}', 'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', @@ -147,7 +133,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin): data.addpart('data', data=json.dumps(settings, separators=(',', ':'))) response = session.post( - f'https://huggingface.co/chat/conversation/{conversationId}', + f'https://huggingface.co/chat/conversation/{conversation.conversation_id}', cookies=session.cookies, headers=headers, multipart=data, @@ -180,4 +166,44 @@ class HuggingChat(AbstractProvider, ProviderModelMixin): full_response = full_response.replace('<|im_end|', '').replace('\u0000', '').strip() if not stream: - yield full_response \ No newline at end of file + yield full_response + + @classmethod + def create_conversation(cls, session: Session, model: str): + json_data = { + 'model': model, + } + response = session.post('https://huggingface.co/chat/conversation', json=json_data) + raise_for_status(response) + + return response.json().get('conversationId') + + @classmethod + def fetch_message_id(cls, session: Session, conversation_id: str): + # Get the data response and parse it properly + response = session.get(f'https://huggingface.co/chat/conversation/{conversation_id}/__data.json?x-sveltekit-invalidated=11') + raise_for_status(response) + + # Split the response content by newlines and parse each line as JSON + try: + json_data = None + for line in response.text.split('\n'): + if line.strip(): + try: + parsed = json.loads(line) + if isinstance(parsed, dict) and "nodes" in parsed: + json_data = parsed + break + except json.JSONDecodeError: + continue + + if not json_data: + raise RuntimeError("Failed to parse response data") + + data = json_data["nodes"][1]["data"] + keys = data[data[0]["messages"]] + message_keys = data[keys[-1]] + return data[message_keys["id"]] + + except (KeyError, IndexError, TypeError) as e: + raise RuntimeError(f"Failed to extract message ID: {str(e)}") \ No newline at end of file diff --git a/g4f/Provider/needs_auth/PollinationsAI.py b/g4f/Provider/needs_auth/PollinationsAI.py index 68cb8cc2..4f4915ee 100644 --- a/g4f/Provider/needs_auth/PollinationsAI.py +++ b/g4f/Provider/needs_auth/PollinationsAI.py @@ -16,6 +16,7 @@ class PollinationsAI(OpenaiAPI): label = "Pollinations.AI" url = "https://pollinations.ai" working = True + needs_auth = False supports_stream = True default_model = "openai" -- cgit v1.2.3