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/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py index 9ad52a94..9c0b8768 100644 --- a/g4f/Provider/needs_auth/OpenaiChat.py +++ b/g4f/Provider/needs_auth/OpenaiChat.py @@ -28,13 +28,13 @@ from ..helper import format_cookies from ..openai.har_file import get_request_config from ..openai.har_file import RequestConfig, arkReq, arkose_url, start_url, conversation_url, backend_url, backend_anon_url from ..openai.proofofwork import generate_proof_token -from ..openai.new import get_requirements_token +from ..openai.new import get_requirements_token, get_config from ... import debug DEFAULT_HEADERS = { "accept": "*/*", "accept-encoding": "gzip, deflate, br, zstd", - "accept-language": "en-US,en;q=0.5", + 'accept-language': 'en-US,en;q=0.8', "referer": "https://chatgpt.com/", "sec-ch-ua": "\"Brave\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"", "sec-ch-ua-mobile": "?0", @@ -46,13 +46,33 @@ DEFAULT_HEADERS = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" } +INIT_HEADERS = { + 'accept': '*/*', + 'accept-language': 'en-US,en;q=0.8', + 'cache-control': 'no-cache', + 'pragma': 'no-cache', + 'priority': 'u=0, i', + 'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', + 'sec-ch-ua-arch': '"arm"', + 'sec-ch-ua-bitness': '"64"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-model': '""', + 'sec-ch-ua-platform': '"macOS"', + 'sec-ch-ua-platform-version': '"14.4.0"', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'none', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1', + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" +} + class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin): """A class for creating and managing conversations with OpenAI chat service""" label = "OpenAI ChatGPT" url = "https://chatgpt.com" working = True - needs_auth = True supports_gpt_4 = True supports_message_history = True supports_system_message = True @@ -127,11 +147,11 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin): image_data["upload_url"], data=data_bytes, headers={ + **DEFAULT_HEADERS, "Content-Type": image_data["mime_type"], "x-ms-blob-type": "BlockBlob", "x-ms-version": "2020-04-08", "Origin": "https://chatgpt.com", - "Referer": "https://chatgpt.com/", } ) as response: await raise_for_status(response) @@ -277,13 +297,20 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin): """ if model == cls.default_image_model: model = cls.default_model - await cls.login(proxy) + if cls.needs_auth: + await cls.login(proxy) async with StreamSession( proxy=proxy, impersonate="chrome", timeout=timeout ) as session: + if not cls.needs_auth: + cls._create_request_args(cookies) + RequestConfig.proof_token = get_config(cls._headers.get("user-agent")) + async with session.get(cls.url, headers=INIT_HEADERS) as response: + cls._update_request_args(session) + await raise_for_status(response) try: image_request = await cls.upload_image(session, cls._headers, image, image_name) if image else None except Exception as e: @@ -553,7 +580,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin): @classmethod def _update_cookie_header(cls): - cls._headers["cookie"] = format_cookies(cls._cookies) + if cls._cookies: + cls._headers["cookie"] = format_cookies(cls._cookies) class Conversation(BaseConversation): """ 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" diff --git a/g4f/Provider/openai/new.py b/g4f/Provider/openai/new.py index f4d8e13d..47943918 100644 --- a/g4f/Provider/openai/new.py +++ b/g4f/Provider/openai/new.py @@ -382,11 +382,11 @@ def get_config(user_agent): config = [ core + screen, get_parse_time(), - 4294705152, + None, random.random(), user_agent, None, - "remix-prod-15f1ec0f78ad898b9606a88d384ef76345b82b82", #document.documentElement.getAttribute("data-build"), + "prod-0b673b9a04fb6983c1417b587f2f31173eafa605", #document.documentElement.getAttribute("data-build"), "en-US", "en-US,es-US,en,es", 0, @@ -395,6 +395,8 @@ def get_config(user_agent): random.choice(window_keys), time.perf_counter(), str(uuid.uuid4()), + "", + 8 ] return config diff --git a/g4f/cli.py b/g4f/cli.py index a8a038a2..617e7cba 100644 --- a/g4f/cli.py +++ b/g4f/cli.py @@ -53,7 +53,8 @@ def run_api_args(args): model=args.model, gui=args.gui, ) - g4f.cookies.browsers = [g4f.cookies[browser] for browser in args.cookie_browsers] + if args.cookie_browsers: + g4f.cookies.browsers = [g4f.cookies[browser] for browser in args.cookie_browsers] run_api( bind=args.bind, port=args.port, diff --git a/g4f/gui/client/static/js/highlightjs-copy.min.js b/g4f/gui/client/static/js/highlightjs-copy.min.js index cd8ae957..87d09026 100644 --- a/g4f/gui/client/static/js/highlightjs-copy.min.js +++ b/g4f/gui/client/static/js/highlightjs-copy.min.js @@ -7,6 +7,9 @@ class CopyButtonPlugin { el, text }) { + if (el.classList.contains("language-plaintext")) { + return; + } let button = Object.assign(document.createElement("button"), { innerHTML: "Copy", className: "hljs-copy-button"