Skip to content

Commit

Permalink
Add RequestLogin to HuggingChat
Browse files Browse the repository at this point in the history
  • Loading branch information
hlohaus committed Jan 23, 2025
1 parent cad3081 commit 5651760
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 49 deletions.
85 changes: 45 additions & 40 deletions g4f/Provider/needs_auth/HuggingChat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,31 @@

import json
import re
import os
import requests
from typing import AsyncIterator

try:
from curl_cffi.requests import Session, CurlMime
has_curl_cffi = True
except ImportError:
has_curl_cffi = False

from ..base_provider import ProviderModelMixin, AbstractProvider
from ..base_provider import ProviderModelMixin, AsyncAuthedProvider, AuthResult
from ..helper import format_prompt
from ...typing import CreateResult, Messages, Cookies
from ...errors import MissingRequirementsError
from ...typing import AsyncResult, Messages, Cookies
from ...errors import MissingRequirementsError, MissingAuthError, ResponseError
from ...requests import get_args_from_nodriver, DEFAULT_HEADERS
from ...requests.raise_for_status import raise_for_status
from ...providers.response import JsonConversation, ImageResponse, Sources, TitleGeneration, Reasoning
from ...providers.response import JsonConversation, ImageResponse, Sources, TitleGeneration, Reasoning, RequestLogin
from ...cookies import get_cookies
from ... import debug

class Conversation(JsonConversation):
def __init__(self, models: dict):
self.models: dict = models

class HuggingChat(AbstractProvider, ProviderModelMixin):
class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
url = "https://huggingface.co/chat"

working = True
Expand Down Expand Up @@ -85,41 +88,44 @@ def add_quotation_mark(match):
return cls.models

@classmethod
def create_completion(
async def on_auth_async(cls, cookies: Cookies = None, proxy: str = None, **kwargs) -> AsyncIterator:
if cookies is None:
cookies = get_cookies("huggingface.co")
if "hf-chat" in cookies:
yield AuthResult(
cookies=cookies,
impersonate="chrome",
headers=DEFAULT_HEADERS
)
return
login_url = os.environ.get("G4F_LOGIN_URL")
if login_url:
yield RequestLogin(cls.__name__, login_url)
yield AuthResult(
**await get_args_from_nodriver(
cls.url,
proxy=proxy,
wait_for='form[action="/chat/logout"]'
)
)

@classmethod
async def create_authed(
cls,
model: str,
messages: Messages,
stream: bool,
auth_result: AuthResult,
prompt: str = None,
return_conversation: bool = False,
conversation: Conversation = None,
web_search: bool = False,
cookies: Cookies = None,
**kwargs
) -> CreateResult:
) -> AsyncResult:
if not has_curl_cffi:
raise MissingRequirementsError('Install "curl_cffi" package | pip install -U curl_cffi')
model = cls.get_model(model)
if cookies is None:
cookies = get_cookies("huggingface.co")

session = Session(cookies=cookies)
session.headers = {
'accept': '*/*',
'accept-language': 'en',
'cache-control': 'no-cache',
'origin': 'https://huggingface.co',
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': 'https://huggingface.co/chat/',
'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"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'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',
}
session = Session(**auth_result.get_dict())

if conversation is None or not hasattr(conversation, "models"):
conversation = Conversation({})
Expand All @@ -133,14 +139,14 @@ def create_completion(
inputs = format_prompt(messages)
else:
conversationId = conversation.models[model]["conversationId"]
conversation.models[model]["message_id"] = cls.fetch_message_id(session, conversationId)
conversation.models[model]["messageId"] = cls.fetch_message_id(session, conversationId)
inputs = messages[-1]["content"]

debug.log(f"Use model {model}: {json.dumps(conversation.models[model])}")
debug.log(f"Use: {json.dumps(conversation.models[model])}")

settings = {
"inputs": inputs,
"id": conversation.models[model]["message_id"],
"id": conversation.models[model]["messageId"],
"is_retry": False,
"is_continue": False,
"web_search": web_search,
Expand Down Expand Up @@ -176,7 +182,6 @@ def create_completion(
)
raise_for_status(response)

full_response = ""
sources = None
for line in response.iter_lines():
if not line:
Expand All @@ -189,10 +194,7 @@ def create_completion(
if "type" not in line:
raise RuntimeError(f"Response: {line}")
elif line["type"] == "stream":
token = line["token"].replace('\u0000', '')
full_response += token
if stream:
yield token
yield line["token"].replace('\u0000', '')
elif line["type"] == "finalAnswer":
break
elif line["type"] == "file":
Expand All @@ -208,9 +210,6 @@ def create_completion(
else:
pass #print(line)

full_response = full_response.replace('<|im_end|', '').strip()
if not stream:
yield full_response
if sources is not None:
yield sources

Expand All @@ -222,8 +221,9 @@ def create_conversation(cls, session: Session, model: str):
'model': model,
}
response = session.post('https://huggingface.co/chat/conversation', json=json_data)
if response.status_code == 401:
raise MissingAuthError(response.text)
raise_for_status(response)

return response.json().get('conversationId')

@classmethod
Expand All @@ -248,6 +248,11 @@ def fetch_message_id(cls, session: Session, conversation_id: str):
if not json_data:
raise RuntimeError("Failed to parse response data")

if json_data["nodes"][-1]["type"] == "error":
if json_data["nodes"][-1]["status"] == 403:
raise MissingAuthError(json_data["nodes"][-1]["error"]["message"])
raise ResponseError(json.dumps(json_data["nodes"][-1]))

data = json_data["nodes"][1]["data"]
keys = data[data[0]["messages"]]
message_keys = data[keys[-1]]
Expand Down
15 changes: 6 additions & 9 deletions g4f/requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,22 @@ async def get_args_from_nodriver(
url: str,
proxy: str = None,
timeout: int = 120,
wait_for: str = None,
cookies: Cookies = None
) -> dict:
if not has_nodriver:
raise MissingRequirementsError('Install "nodriver" package | pip install -U nodriver')
browser = await get_nodriver(proxy=proxy)
if debug.logging:
print(f"Open nodriver with url: {url}")
browser = await nodriver.start(
browser_args=None if proxy is None else [f"--proxy-server={proxy}"],
)
domain = urlparse(url).netloc
if cookies is None:
cookies = {}
else:
await browser.cookies.set_all(get_cookie_params_from_dict(cookies, url=url, domain=domain))
page = await browser.get(url)
for c in await page.send(nodriver.cdp.network.get_cookies([url])):
cookies[c.name] = c.value
user_agent = await page.evaluate("window.navigator.userAgent")
await page.wait_for("body:not(.no-js)", timeout=timeout)
if wait_for is not None:
await page.wait_for(wait_for, timeout=timeout)
for c in await page.send(nodriver.cdp.network.get_cookies([url])):
cookies[c.name] = c.value
await page.close()
Expand All @@ -120,13 +117,13 @@ def merge_cookies(cookies: Iterator[Morsel], response: Response) -> Cookies:

async def get_nodriver(proxy: str = None, user_data_dir = "nodriver", browser_executable_path=None, **kwargs)-> Browser:
if not has_nodriver:
raise MissingRequirementsError('Install "nodriver" package | pip install -U nodriver')
raise MissingRequirementsError('Install "nodriver" and "platformdirs" package | pip install -U nodriver platformdirs')
user_data_dir = user_config_dir(f"g4f-{user_data_dir}") if has_platformdirs else None
if browser_executable_path is None:
try:
browser_executable_path = find_chrome_executable()
except FileNotFoundError:
# Default to Edge if Chrome is not found
# Default to Edge if Chrome is not available.
browser_executable_path = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"
if not os.path.exists(browser_executable_path):
browser_executable_path = None
Expand Down

0 comments on commit 5651760

Please sign in to comment.