From 89e096334dfb1049e8cb427a866739d934cd7ad8 Mon Sep 17 00:00:00 2001
From: hlohaus <983577+hlohaus@users.noreply.github.com>
Date: Fri, 31 Jan 2025 17:36:48 +0100
Subject: Support reasoning tokens by default Add new default HuggingFace
provider Add format_image_prompt and get_last_user_message helper Add
stop_browser callable to get_nodriver function Fix content type response in
images route
---
g4f/Provider/Blackbox.py | 8 +-
g4f/Provider/Copilot.py | 7 +-
g4f/Provider/OIVSCode.py | 5 +-
g4f/Provider/PerplexityLabs.py | 19 +-
g4f/Provider/PollinationsAI.py | 4 +-
g4f/Provider/You.py | 4 +-
g4f/Provider/__init__.py | 1 +
g4f/Provider/hf/HuggingChat.py | 226 ++++++++++++++++++
g4f/Provider/hf/HuggingFaceAPI.py | 61 +++++
g4f/Provider/hf/HuggingFaceInference.py | 215 +++++++++++++++++
g4f/Provider/hf/__init__.py | 62 +++++
g4f/Provider/hf/models.py | 46 ++++
g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py | 3 +-
.../hf_space/BlackForestLabsFlux1Schnell.py | 3 +-
g4f/Provider/hf_space/CohereForAI.py | 5 +-
g4f/Provider/hf_space/Janus_Pro_7B.py | 60 ++---
g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py | 3 +-
g4f/Provider/hf_space/StableDiffusion35Large.py | 3 +-
g4f/Provider/hf_space/VoodoohopFlux1Schnell.py | 6 +-
g4f/Provider/mini_max/HailuoAI.py | 3 +-
g4f/Provider/needs_auth/Anthropic.py | 2 +-
g4f/Provider/needs_auth/BingCreateImages.py | 3 +-
g4f/Provider/needs_auth/DeepInfra.py | 3 +-
g4f/Provider/needs_auth/DeepSeekAPI.py | 2 +-
g4f/Provider/needs_auth/Gemini.py | 7 +-
g4f/Provider/needs_auth/GithubCopilot.py | 4 +-
g4f/Provider/needs_auth/HuggingChat.py | 265 ---------------------
g4f/Provider/needs_auth/HuggingFace.py | 220 -----------------
g4f/Provider/needs_auth/HuggingFaceAPI.py | 57 -----
g4f/Provider/needs_auth/MicrosoftDesigner.py | 8 +-
g4f/Provider/needs_auth/OpenaiChat.py | 6 +-
g4f/Provider/needs_auth/__init__.py | 3 -
g4f/Provider/template/BackendApi.py | 13 +-
g4f/Provider/template/OpenaiTemplate.py | 18 +-
34 files changed, 701 insertions(+), 654 deletions(-)
create mode 100644 g4f/Provider/hf/HuggingChat.py
create mode 100644 g4f/Provider/hf/HuggingFaceAPI.py
create mode 100644 g4f/Provider/hf/HuggingFaceInference.py
create mode 100644 g4f/Provider/hf/__init__.py
create mode 100644 g4f/Provider/hf/models.py
delete mode 100644 g4f/Provider/needs_auth/HuggingChat.py
delete mode 100644 g4f/Provider/needs_auth/HuggingFace.py
delete mode 100644 g4f/Provider/needs_auth/HuggingFaceAPI.py
(limited to 'g4f/Provider')
diff --git a/g4f/Provider/Blackbox.py b/g4f/Provider/Blackbox.py
index 10abded5..d57d27c9 100644
--- a/g4f/Provider/Blackbox.py
+++ b/g4f/Provider/Blackbox.py
@@ -13,7 +13,7 @@ from ..requests.raise_for_status import raise_for_status
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..image import ImageResponse, to_data_uri
from ..cookies import get_cookies_dir
-from .helper import format_prompt
+from .helper import format_prompt, format_image_prompt
from ..providers.response import FinishReason, JsonConversation, Reasoning
class Conversation(JsonConversation):
@@ -216,9 +216,8 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
async with ClientSession(headers=headers) as session:
if model == "ImageGeneration2":
- prompt = messages[-1]["content"]
data = {
- "query": prompt,
+ "query": format_image_prompt(messages, prompt),
"agentMode": True
}
headers['content-type'] = 'text/plain;charset=UTF-8'
@@ -307,8 +306,7 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
image_url_match = re.search(r'!\[.*?\]\((.*?)\)', text_to_yield)
if image_url_match:
image_url = image_url_match.group(1)
- prompt = messages[-1]["content"]
- yield ImageResponse(images=[image_url], alt=prompt)
+ yield ImageResponse(image_url, format_image_prompt(messages, prompt))
else:
if "" in text_to_yield and "" in chunk_text :
chunk_text = text_to_yield.split('', 1)
diff --git a/g4f/Provider/Copilot.py b/g4f/Provider/Copilot.py
index 8e961fb5..f63b0563 100644
--- a/g4f/Provider/Copilot.py
+++ b/g4f/Provider/Copilot.py
@@ -28,6 +28,7 @@ from ..providers.response import BaseConversation, JsonConversation, RequestLogi
from ..providers.asyncio import get_running_loop
from ..requests import get_nodriver
from ..image import ImageResponse, to_bytes, is_accepted_format
+from .helper import get_last_user_message
from .. import debug
class Conversation(JsonConversation):
@@ -139,7 +140,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
else:
conversation_id = conversation.conversation_id
if prompt is None:
- prompt = messages[-1]["content"]
+ prompt = get_last_user_message(messages)
debug.log(f"Copilot: Use conversation: {conversation_id}")
uploaded_images = []
@@ -206,7 +207,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
yield Parameters(**{"cookies": {c.name: c.value for c in session.cookies.jar}})
async def get_access_token_and_cookies(url: str, proxy: str = None, target: str = "ChatAI",):
- browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
+ browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
try:
page = await browser.get(url)
access_token = None
@@ -233,7 +234,7 @@ async def get_access_token_and_cookies(url: str, proxy: str = None, target: str
await page.close()
return access_token, cookies
finally:
- browser.stop()
+ stop_browser()
def readHAR(url: str):
api_key = None
diff --git a/g4f/Provider/OIVSCode.py b/g4f/Provider/OIVSCode.py
index ef88c5ed..fae8c179 100644
--- a/g4f/Provider/OIVSCode.py
+++ b/g4f/Provider/OIVSCode.py
@@ -9,7 +9,6 @@ class OIVSCode(OpenaiTemplate):
working = True
needs_auth = False
- default_model = "gpt-4o-mini-2024-07-18"
+ default_model = "gpt-4o-mini"
default_vision_model = default_model
- vision_models = [default_model, "gpt-4o-mini"]
- model_aliases = {"gpt-4o-mini": "gpt-4o-mini-2024-07-18"}
\ No newline at end of file
+ vision_models = [default_model, "gpt-4o-mini"]
\ No newline at end of file
diff --git a/g4f/Provider/PerplexityLabs.py b/g4f/Provider/PerplexityLabs.py
index a1050575..d8477064 100644
--- a/g4f/Provider/PerplexityLabs.py
+++ b/g4f/Provider/PerplexityLabs.py
@@ -5,7 +5,7 @@ import json
from ..typing import AsyncResult, Messages
from ..requests import StreamSession, raise_for_status
-from ..providers.response import Reasoning, FinishReason
+from ..providers.response import FinishReason
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
API_URL = "https://www.perplexity.ai/socket.io/"
@@ -87,22 +87,7 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
continue
try:
data = json.loads(message[2:])[1]
- new_content = data["output"][last_message:]
-
- if "" in new_content:
- yield Reasoning(None, "thinking")
- is_thinking = True
- if "" in new_content:
- new_content = new_content.split("", 1)
- yield Reasoning(f"{new_content[0]}")
- yield Reasoning(None, "finished")
- yield new_content[1]
- is_thinking = False
- elif is_thinking:
- yield Reasoning(new_content)
- else:
- yield new_content
-
+ yield data["output"][last_message:]
last_message = len(data["output"])
if data["final"]:
yield FinishReason("stop")
diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py
index f8664801..12268288 100644
--- a/g4f/Provider/PollinationsAI.py
+++ b/g4f/Provider/PollinationsAI.py
@@ -7,7 +7,7 @@ from urllib.parse import quote_plus
from typing import Optional
from aiohttp import ClientSession
-from .helper import filter_none
+from .helper import filter_none, format_image_prompt
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..typing import AsyncResult, Messages, ImagesType
from ..image import to_data_uri
@@ -127,7 +127,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
if model in cls.image_models:
yield await cls._generate_image(
model=model,
- prompt=messages[-1]["content"] if prompt is None else prompt,
+ prompt=format_image_prompt(messages, prompt),
proxy=proxy,
width=width,
height=height,
diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py
index 7e5df0fd..52546280 100644
--- a/g4f/Provider/You.py
+++ b/g4f/Provider/You.py
@@ -77,7 +77,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
except MissingRequirementsError:
pass
if not cookies or "afUserId" not in cookies:
- browser = await get_nodriver(proxy=proxy)
+ browser, stop_browser = await get_nodriver(proxy=proxy)
try:
page = await browser.get(cls.url)
await page.wait_for('[data-testid="user-profile-button"]', timeout=900)
@@ -86,7 +86,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
cookies[c.name] = c.value
await page.close()
finally:
- browser.stop()
+ stop_browser()
async with StreamSession(
proxy=proxy,
impersonate="chrome",
diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py
index 0e0b2f4d..55ee07e0 100644
--- a/g4f/Provider/__init__.py
+++ b/g4f/Provider/__init__.py
@@ -9,6 +9,7 @@ from .deprecated import *
from .needs_auth import *
from .not_working import *
from .local import *
+from .hf import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference
from .hf_space import HuggingSpace
from .mini_max import HailuoAI, MiniMax
from .template import OpenaiTemplate, BackendApi
diff --git a/g4f/Provider/hf/HuggingChat.py b/g4f/Provider/hf/HuggingChat.py
new file mode 100644
index 00000000..29f5b9c6
--- /dev/null
+++ b/g4f/Provider/hf/HuggingChat.py
@@ -0,0 +1,226 @@
+from __future__ import annotations
+
+import json
+import re
+import os
+import requests
+import base64
+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, AsyncAuthedProvider, AuthResult
+from ..helper import format_prompt, format_image_prompt, get_last_user_message
+from ...typing import AsyncResult, Messages, Cookies, ImagesType
+from ...errors import MissingRequirementsError, MissingAuthError, ResponseError
+from ...image import to_bytes
+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, RequestLogin
+from ...cookies import get_cookies
+from .models import default_model, fallback_models, image_models, model_aliases
+from ... import debug
+
+class Conversation(JsonConversation):
+ def __init__(self, models: dict):
+ self.models: dict = models
+
+class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
+ url = "https://huggingface.co/chat"
+
+ working = True
+ use_nodriver = True
+ supports_stream = True
+ needs_auth = True
+ default_model = default_model
+ model_aliases = model_aliases
+ image_models = image_models
+
+ @classmethod
+ def get_models(cls):
+ if not cls.models:
+ try:
+ text = requests.get(cls.url).text
+ text = re.sub(r',parameters:{[^}]+?}', '', text)
+ text = re.search(r'models:(\[.+?\]),oldModels:', text).group(1)
+ text = text.replace('void 0', 'null')
+ def add_quotation_mark(match):
+ return f'{match.group(1)}"{match.group(2)}":'
+ text = re.sub(r'([{,])([A-Za-z0-9_]+?):', add_quotation_mark, text)
+ models = json.loads(text)
+ cls.text_models = [model["id"] for model in models]
+ cls.models = cls.text_models + cls.image_models
+ cls.vision_models = [model["id"] for model in models if model["multimodal"]]
+ except Exception as e:
+ debug.log(f"HuggingChat: Error reading models: {type(e).__name__}: {e}")
+ cls.models = [*fallback_models]
+ return cls.models
+
+ @classmethod
+ async def on_auth_async(cls, cookies: Cookies = None, proxy: str = None, **kwargs) -> AsyncIterator:
+ if cookies is None:
+ cookies = get_cookies("huggingface.co", single_browser=True)
+ if "hf-chat" in cookies:
+ yield AuthResult(
+ cookies=cookies,
+ impersonate="chrome",
+ headers=DEFAULT_HEADERS
+ )
+ return
+ yield RequestLogin(cls.__name__, os.environ.get("G4F_LOGIN_URL") or "")
+ 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,
+ auth_result: AuthResult,
+ prompt: str = None,
+ images: ImagesType = None,
+ return_conversation: bool = False,
+ conversation: Conversation = None,
+ web_search: bool = False,
+ **kwargs
+ ) -> AsyncResult:
+ if not has_curl_cffi:
+ raise MissingRequirementsError('Install "curl_cffi" package | pip install -U curl_cffi')
+ model = cls.get_model(model)
+
+ session = Session(**auth_result.get_dict())
+
+ if conversation is None or not hasattr(conversation, "models"):
+ conversation = Conversation({})
+
+ if model not in conversation.models:
+ conversationId = cls.create_conversation(session, model)
+ debug.log(f"Conversation created: {json.dumps(conversationId[8:] + '...')}")
+ messageId = cls.fetch_message_id(session, conversationId)
+ conversation.models[model] = {"conversationId": conversationId, "messageId": messageId}
+ if return_conversation:
+ yield conversation
+ inputs = format_prompt(messages)
+ else:
+ conversationId = conversation.models[model]["conversationId"]
+ conversation.models[model]["messageId"] = cls.fetch_message_id(session, conversationId)
+ inputs = get_last_user_message(messages)
+
+ settings = {
+ "inputs": inputs,
+ "id": conversation.models[model]["messageId"],
+ "is_retry": False,
+ "is_continue": False,
+ "web_search": web_search,
+ "tools": ["000000000000000000000001"] if model in cls.image_models else [],
+ }
+
+ headers = {
+ 'accept': '*/*',
+ 'origin': 'https://huggingface.co',
+ 'referer': f'https://huggingface.co/chat/conversation/{conversationId}',
+ }
+ data = CurlMime()
+ data.addpart('data', data=json.dumps(settings, separators=(',', ':')))
+ if images is not None:
+ for image, filename in images:
+ data.addpart(
+ "files",
+ filename=f"base64;{filename}",
+ data=base64.b64encode(to_bytes(image))
+ )
+
+ response = session.post(
+ f'https://huggingface.co/chat/conversation/{conversationId}',
+ headers=headers,
+ multipart=data,
+ stream=True
+ )
+ raise_for_status(response)
+
+ sources = None
+ for line in response.iter_lines():
+ if not line:
+ continue
+ try:
+ line = json.loads(line)
+ except json.JSONDecodeError as e:
+ debug.log(f"Failed to decode JSON: {line}, error: {e}")
+ continue
+ if "type" not in line:
+ raise RuntimeError(f"Response: {line}")
+ elif line["type"] == "stream":
+ yield line["token"].replace('\u0000', '')
+ elif line["type"] == "finalAnswer":
+ break
+ elif line["type"] == "file":
+ url = f"https://huggingface.co/chat/conversation/{conversationId}/output/{line['sha']}"
+ yield ImageResponse(url, format_image_prompt(messages, prompt), options={"cookies": auth_result.cookies})
+ elif line["type"] == "webSearch" and "sources" in line:
+ sources = Sources(line["sources"])
+ elif line["type"] == "title":
+ yield TitleGeneration(line["title"])
+ elif line["type"] == "reasoning":
+ yield Reasoning(line.get("token"), line.get("status"))
+
+ if sources is not None:
+ yield sources
+
+ @classmethod
+ def create_conversation(cls, session: Session, model: str):
+ if model in cls.image_models:
+ model = cls.default_model
+ json_data = {
+ 'model': model,
+ }
+ response = session.post('https://huggingface.co/chat/conversation', json=json_data)
+ if response.status_code == 401:
+ raise MissingAuthError(response.text)
+ if response.status_code == 400:
+ raise ResponseError(f"{response.text}: Model: {model}")
+ 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")
+
+ 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]]
+ return data[message_keys["id"]]
+
+ except (KeyError, IndexError, TypeError) as e:
+ raise RuntimeError(f"Failed to extract message ID: {str(e)}")
diff --git a/g4f/Provider/hf/HuggingFaceAPI.py b/g4f/Provider/hf/HuggingFaceAPI.py
new file mode 100644
index 00000000..fdbb1f7c
--- /dev/null
+++ b/g4f/Provider/hf/HuggingFaceAPI.py
@@ -0,0 +1,61 @@
+from __future__ import annotations
+
+from ..template.OpenaiTemplate import OpenaiTemplate
+from .models import model_aliases
+from ...providers.types import Messages
+from .HuggingChat import HuggingChat
+from ... import debug
+
+class HuggingFaceAPI(OpenaiTemplate):
+ label = "HuggingFace (Inference API)"
+ parent = "HuggingFace"
+ url = "https://api-inference.huggingface.com"
+ api_base = "https://api-inference.huggingface.co/v1"
+ working = True
+ needs_auth = True
+
+ default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
+ default_vision_model = default_model
+ vision_models = [default_vision_model, "Qwen/Qwen2-VL-7B-Instruct"]
+ model_aliases = model_aliases
+
+ @classmethod
+ def get_models(cls, **kwargs):
+ if not cls.models:
+ HuggingChat.get_models()
+ cls.models = HuggingChat.text_models.copy()
+ for model in cls.vision_models:
+ if model not in cls.models:
+ cls.models.append(model)
+ return cls.models
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ api_base: str = None,
+ max_tokens: int = 2048,
+ max_inputs_lenght: int = 10000,
+ **kwargs
+ ):
+ if api_base is None:
+ model_name = model
+ if model in cls.model_aliases:
+ model_name = cls.model_aliases[model]
+ api_base = f"https://api-inference.huggingface.co/models/{model_name}/v1"
+ start = calculate_lenght(messages)
+ if start > max_inputs_lenght:
+ if len(messages) > 6:
+ messages = messages[:3] + messages[-3:]
+ if calculate_lenght(messages) > max_inputs_lenght:
+ if len(messages) > 2:
+ messages = [m for m in messages if m["role"] == "system"] + messages[-1:]
+ if len(messages) > 1 and calculate_lenght(messages) > max_inputs_lenght:
+ messages = [messages[-1]]
+ debug.log(f"Messages trimmed from: {start} to: {calculate_lenght(messages)}")
+ async for chunk in super().create_async_generator(model, messages, api_base=api_base, max_tokens=max_tokens, **kwargs):
+ yield chunk
+
+def calculate_lenght(messages: Messages) -> int:
+ return sum([len(message["content"]) + 16 for message in messages])
\ No newline at end of file
diff --git a/g4f/Provider/hf/HuggingFaceInference.py b/g4f/Provider/hf/HuggingFaceInference.py
new file mode 100644
index 00000000..df866ba3
--- /dev/null
+++ b/g4f/Provider/hf/HuggingFaceInference.py
@@ -0,0 +1,215 @@
+from __future__ import annotations
+
+import json
+import base64
+import random
+import requests
+
+from ...typing import AsyncResult, Messages
+from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_prompt
+from ...errors import ModelNotFoundError, ModelNotSupportedError, ResponseError
+from ...requests import StreamSession, raise_for_status
+from ...providers.response import FinishReason
+from ...image import ImageResponse
+from ..helper import format_image_prompt
+from .models import default_model, default_image_model, model_aliases, fallback_models
+from ... import debug
+
+class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin):
+ url = "https://huggingface.co"
+ working = True
+
+ default_model = default_model
+ default_image_model = default_image_model
+ model_aliases = model_aliases
+
+ @classmethod
+ def get_models(cls) -> list[str]:
+ if not cls.models:
+ models = fallback_models.copy()
+ url = "https://huggingface.co/api/models?inference=warm&pipeline_tag=text-generation"
+ extra_models = [model["id"] for model in requests.get(url).json()]
+ extra_models.sort()
+ models.extend([model for model in extra_models if model not in models])
+ if not cls.image_models:
+ url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
+ cls.image_models = [model["id"] for model in requests.get(url).json() if model["trendingScore"] >= 20]
+ cls.image_models.sort()
+ models.extend([model for model in cls.image_models if model not in models])
+ cls.models = models
+ return cls.models
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ stream: bool = True,
+ proxy: str = None,
+ api_base: str = "https://api-inference.huggingface.co",
+ api_key: str = None,
+ max_tokens: int = 1024,
+ temperature: float = None,
+ prompt: str = None,
+ action: str = None,
+ extra_data: dict = {},
+ **kwargs
+ ) -> AsyncResult:
+ try:
+ model = cls.get_model(model)
+ except ModelNotSupportedError:
+ pass
+ 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',
+ }
+ if api_key is not None:
+ headers["Authorization"] = f"Bearer {api_key}"
+ payload = None
+ if cls.get_models() and model in cls.image_models:
+ stream = False
+ prompt = format_image_prompt(messages, prompt)
+ payload = {"inputs": prompt, "parameters": {"seed": random.randint(0, 2**32), **extra_data}}
+ else:
+ params = {
+ "return_full_text": False,
+ "max_new_tokens": max_tokens,
+ "temperature": temperature,
+ **extra_data
+ }
+ do_continue = action == "continue"
+ async with StreamSession(
+ headers=headers,
+ proxy=proxy,
+ timeout=600
+ ) as session:
+ if payload is None:
+ async with session.get(f"https://huggingface.co/api/models/{model}") as response:
+ if response.status == 404:
+ raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__}")
+ await raise_for_status(response)
+ model_data = await response.json()
+ model_type = None
+ if "config" in model_data and "model_type" in model_data["config"]:
+ model_type = model_data["config"]["model_type"]
+ debug.log(f"Model type: {model_type}")
+ inputs = get_inputs(messages, model_data, model_type, do_continue)
+ debug.log(f"Inputs len: {len(inputs)}")
+ if len(inputs) > 4096:
+ if len(messages) > 6:
+ messages = messages[:3] + messages[-3:]
+ else:
+ messages = [m for m in messages if m["role"] == "system"] + [messages[-1]]
+ inputs = get_inputs(messages, model_data, model_type, do_continue)
+ debug.log(f"New len: {len(inputs)}")
+ if model_type == "gpt2" and max_tokens >= 1024:
+ params["max_new_tokens"] = 512
+ payload = {"inputs": inputs, "parameters": params, "stream": stream}
+
+ async with session.post(f"{api_base.rstrip('/')}/models/{model}", json=payload) as response:
+ if response.status == 404:
+ raise ModelNotFoundError(f"Model is not supported: {model}")
+ await raise_for_status(response)
+ if stream:
+ first = True
+ is_special = False
+ async for line in response.iter_lines():
+ if line.startswith(b"data:"):
+ data = json.loads(line[5:])
+ if "error" in data:
+ raise ResponseError(data["error"])
+ if not data["token"]["special"]:
+ chunk = data["token"]["text"]
+ if first and not do_continue:
+ first = False
+ chunk = chunk.lstrip()
+ if chunk:
+ yield chunk
+ else:
+ is_special = True
+ debug.log(f"Special token: {is_special}")
+ yield FinishReason("stop" if is_special else "length")
+ else:
+ if response.headers["content-type"].startswith("image/"):
+ base64_data = base64.b64encode(b"".join([chunk async for chunk in response.iter_content()]))
+ url = f"data:{response.headers['content-type']};base64,{base64_data.decode()}"
+ yield ImageResponse(url, prompt)
+ else:
+ yield (await response.json())[0]["generated_text"].strip()
+
+def format_prompt_mistral(messages: Messages, do_continue: bool = False) -> str:
+ system_messages = [message["content"] for message in messages if message["role"] == "system"]
+ question = " ".join([messages[-1]["content"], *system_messages])
+ history = "\n".join([
+ f"[INST]{messages[idx-1]['content']} [/INST] {message['content']}"
+ for idx, message in enumerate(messages)
+ if message["role"] == "assistant"
+ ])
+ if do_continue:
+ return history[:-len('')]
+ return f"{history}\n[INST] {question} [/INST]"
+
+def format_prompt_qwen(messages: Messages, do_continue: bool = False) -> str:
+ prompt = "".join([
+ f"<|im_start|>{message['role']}\n{message['content']}\n<|im_end|>\n" for message in messages
+ ]) + ("" if do_continue else "<|im_start|>assistant\n")
+ if do_continue:
+ return prompt[:-len("\n<|im_end|>\n")]
+ return prompt
+
+def format_prompt_qwen2(messages: Messages, do_continue: bool = False) -> str:
+ prompt = "".join([
+ f"\u003C|{message['role'].capitalize()}|\u003E{message['content']}\u003C|end▁of▁sentence|\u003E" for message in messages
+ ]) + ("" if do_continue else "\u003C|Assistant|\u003E")
+ if do_continue:
+ return prompt[:-len("\u003C|Assistant|\u003E")]
+ return prompt
+
+def format_prompt_llama(messages: Messages, do_continue: bool = False) -> str:
+ prompt = "<|begin_of_text|>" + "".join([
+ f"<|start_header_id|>{message['role']}<|end_header_id|>\n\n{message['content']}\n<|eot_id|>\n" for message in messages
+ ]) + ("" if do_continue else "<|start_header_id|>assistant<|end_header_id|>\n\n")
+ if do_continue:
+ return prompt[:-len("\n<|eot_id|>\n")]
+ return prompt
+
+def format_prompt_custom(messages: Messages, end_token: str = "", do_continue: bool = False) -> str:
+ prompt = "".join([
+ f"<|{message['role']}|>\n{message['content']}{end_token}\n" for message in messages
+ ]) + ("" if do_continue else "<|assistant|>\n")
+ if do_continue:
+ return prompt[:-len(end_token + "\n")]
+ return prompt
+
+def get_inputs(messages: Messages, model_data: dict, model_type: str, do_continue: bool = False) -> str:
+ if model_type in ("gpt2", "gpt_neo", "gemma", "gemma2"):
+ inputs = format_prompt(messages, do_continue=do_continue)
+ elif model_type == "mistral" and model_data.get("author") == "mistralai":
+ inputs = format_prompt_mistral(messages, do_continue)
+ elif "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
+ eos_token = model_data["config"]["tokenizer_config"]["eos_token"]
+ if eos_token in ("<|endoftext|>", "", ""):
+ inputs = format_prompt_custom(messages, eos_token, do_continue)
+ elif eos_token == "<|im_end|>":
+ inputs = format_prompt_qwen(messages, do_continue)
+ elif "content" in eos_token and eos_token["content"] == "\u003C|end▁of▁sentence|\u003E":
+ inputs = format_prompt_qwen2(messages, do_continue)
+ elif eos_token == "<|eot_id|>":
+ inputs = format_prompt_llama(messages, do_continue)
+ else:
+ inputs = format_prompt(messages, do_continue=do_continue)
+ else:
+ inputs = format_prompt(messages, do_continue=do_continue)
+ return inputs
\ No newline at end of file
diff --git a/g4f/Provider/hf/__init__.py b/g4f/Provider/hf/__init__.py
new file mode 100644
index 00000000..dbc11892
--- /dev/null
+++ b/g4f/Provider/hf/__init__.py
@@ -0,0 +1,62 @@
+from __future__ import annotations
+
+import random
+
+from ...typing import AsyncResult, Messages
+from ...providers.response import ImageResponse
+from ...errors import ModelNotSupportedError
+from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from .HuggingChat import HuggingChat
+from .HuggingFaceAPI import HuggingFaceAPI
+from .HuggingFaceInference import HuggingFaceInference
+from ... import debug
+
+class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
+ url = "https://huggingface.co"
+ login_url = "https://huggingface.co/settings/tokens"
+ working = True
+ supports_message_history = True
+
+ @classmethod
+ def get_models(cls) -> list[str]:
+ if not cls.models:
+ cls.models = HuggingFaceInference.get_models()
+ cls.image_models = HuggingFaceInference.image_models
+ return cls.models
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ **kwargs
+ ) -> AsyncResult:
+ if "api_key" not in kwargs and "images" not in kwargs and random.random() >= 0.5:
+ try:
+ is_started = False
+ async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+ if isinstance(chunk, (str, ImageResponse)):
+ is_started = True
+ yield chunk
+ if is_started:
+ return
+ except Exception as e:
+ if is_started:
+ raise e
+ debug.log(f"Inference failed: {e.__class__.__name__}: {e}")
+ if not cls.image_models:
+ cls.get_models()
+ if model in cls.image_models:
+ if "api_key" not in kwargs:
+ async for chunk in HuggingChat.create_async_generator(model, messages, **kwargs):
+ yield chunk
+ else:
+ async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+ yield chunk
+ return
+ try:
+ async for chunk in HuggingFaceAPI.create_async_generator(model, messages, **kwargs):
+ yield chunk
+ except ModelNotSupportedError:
+ async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+ yield chunk
\ No newline at end of file
diff --git a/g4f/Provider/hf/models.py b/g4f/Provider/hf/models.py
new file mode 100644
index 00000000..00c652f7
--- /dev/null
+++ b/g4f/Provider/hf/models.py
@@ -0,0 +1,46 @@
+default_model = "Qwen/Qwen2.5-72B-Instruct"
+default_image_model = "black-forest-labs/FLUX.1-dev"
+image_models = [
+ default_image_model,
+ "black-forest-labs/FLUX.1-schnell",
+]
+fallback_models = [
+ default_model,
+ 'meta-llama/Llama-3.3-70B-Instruct',
+ 'CohereForAI/c4ai-command-r-plus-08-2024',
+ 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
+ 'Qwen/QwQ-32B-Preview',
+ 'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
+ 'Qwen/Qwen2.5-Coder-32B-Instruct',
+ 'meta-llama/Llama-3.2-11B-Vision-Instruct',
+ 'mistralai/Mistral-Nemo-Instruct-2407',
+ 'microsoft/Phi-3.5-mini-instruct',
+] + image_models
+model_aliases = {
+ ### Chat ###
+ "qwen-2.5-72b": "Qwen/Qwen2.5-Coder-32B-Instruct",
+ "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct",
+ "command-r-plus": "CohereForAI/c4ai-command-r-plus-08-2024",
+ "deepseek-r1": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
+ "qwq-32b": "Qwen/QwQ-32B-Preview",
+ "nemotron-70b": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
+ "qwen-2.5-coder-32b": "Qwen/Qwen2.5-Coder-32B-Instruct",
+ "llama-3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct",
+ "mistral-nemo": "mistralai/Mistral-Nemo-Instruct-2407",
+ "phi-3.5-mini": "microsoft/Phi-3.5-mini-instruct",
+ ### Image ###
+ "flux": "black-forest-labs/FLUX.1-dev",
+ "flux-dev": "black-forest-labs/FLUX.1-dev",
+ "flux-schnell": "black-forest-labs/FLUX.1-schnell",
+ ### Used in other providers ###
+ "qwen-2-vl-7b": "Qwen/Qwen2-VL-7B-Instruct",
+ "gemma-2-27b": "google/gemma-2-27b-it",
+ "qwen-2-72b": "Qwen/Qwen2-72B-Instruct",
+ "qvq-72b": "Qwen/QVQ-72B-Preview",
+ "sd-3.5": "stabilityai/stable-diffusion-3.5-large",
+}
+extra_models = [
+ "meta-llama/Llama-3.2-11B-Vision-Instruct",
+ "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
+ "NousResearch/Hermes-3-Llama-3.1-8B",
+]
\ No newline at end of file
diff --git a/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py b/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
index b4357bff..48add133 100644
--- a/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
+++ b/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
@@ -7,6 +7,7 @@ from ...typing import AsyncResult, Messages
from ...image import ImageResponse, ImagePreview
from ...errors import ResponseError
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
class BlackForestLabsFlux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://black-forest-labs-flux-1-dev.hf.space"
@@ -44,7 +45,7 @@ class BlackForestLabsFlux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
if api_key is not None:
headers["Authorization"] = f"Bearer {api_key}"
async with ClientSession(headers=headers) as session:
- prompt = messages[-1]["content"] if prompt is None else prompt
+ prompt = format_image_prompt(messages, prompt)
data = {
"data": [prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
}
diff --git a/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py b/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
index 11d2ee8e..2a1a2583 100644
--- a/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
+++ b/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
@@ -8,6 +8,7 @@ from ...image import ImageResponse
from ...errors import ResponseError
from ...requests.raise_for_status import raise_for_status
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
class BlackForestLabsFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://black-forest-labs-flux-1-schnell.hf.space"
@@ -42,7 +43,7 @@ class BlackForestLabsFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
height = max(32, height - (height % 8))
if prompt is None:
- prompt = messages[-1]["content"]
+ prompt = format_image_prompt(messages)
payload = {
"data": [
diff --git a/g4f/Provider/hf_space/CohereForAI.py b/g4f/Provider/hf_space/CohereForAI.py
index 8fda88a5..1ecdd8f1 100644
--- a/g4f/Provider/hf_space/CohereForAI.py
+++ b/g4f/Provider/hf_space/CohereForAI.py
@@ -6,7 +6,7 @@ from aiohttp import ClientSession, FormData
from ...typing import AsyncResult, Messages
from ...requests import raise_for_status
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import format_prompt
+from ..helper import format_prompt, get_last_user_message
from ...providers.response import JsonConversation, TitleGeneration
class CohereForAI(AsyncGeneratorProvider, ProviderModelMixin):
@@ -58,7 +58,7 @@ class CohereForAI(AsyncGeneratorProvider, ProviderModelMixin):
) as session:
system_prompt = "\n".join([message["content"] for message in messages if message["role"] == "system"])
messages = [message for message in messages if message["role"] != "system"]
- inputs = format_prompt(messages) if conversation is None else messages[-1]["content"]
+ inputs = format_prompt(messages) if conversation is None else get_last_user_message(messages)
if conversation is None or conversation.model != model or conversation.preprompt != system_prompt:
data = {"model": model, "preprompt": system_prompt}
async with session.post(cls.conversation_url, json=data, proxy=proxy) as response:
@@ -78,7 +78,6 @@ class CohereForAI(AsyncGeneratorProvider, ProviderModelMixin):
data = node["data"]
message_id = data[data[data[data[0]["messages"]][-1]]["id"]]
data = FormData()
- inputs = messages[-1]["content"]
data.add_field(
"data",
json.dumps({"inputs": inputs, "id": message_id, "is_retry": False, "is_continue": False, "web_search": False, "tools": []}),
diff --git a/g4f/Provider/hf_space/Janus_Pro_7B.py b/g4f/Provider/hf_space/Janus_Pro_7B.py
index 43e5f518..92be38a0 100644
--- a/g4f/Provider/hf_space/Janus_Pro_7B.py
+++ b/g4f/Provider/hf_space/Janus_Pro_7B.py
@@ -8,8 +8,8 @@ import urllib.parse
from ...typing import AsyncResult, Messages, Cookies
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import format_prompt
-from ...providers.response import JsonConversation, ImageResponse
+from ..helper import format_prompt, format_image_prompt
+from ...providers.response import JsonConversation, ImageResponse, Notification
from ...requests.aiohttp import StreamSession, StreamResponse
from ...requests.raise_for_status import raise_for_status
from ...cookies import get_cookies
@@ -38,7 +38,7 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
"headers": {
"content-type": "application/json",
"x-zerogpu-token": conversation.zerogpu_token,
- "x-zerogpu-uuid": conversation.uuid,
+ "x-zerogpu-uuid": conversation.zerogpu_uuid,
"referer": cls.referer,
},
"json": {"data":[None,prompt,42,0.95,0.1],"event_data":None,"fn_index":2,"trigger_id":10,"session_hash":conversation.session_hash},
@@ -48,7 +48,7 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
"headers": {
"content-type": "application/json",
"x-zerogpu-token": conversation.zerogpu_token,
- "x-zerogpu-uuid": conversation.uuid,
+ "x-zerogpu-uuid": conversation.zerogpu_uuid,
"referer": cls.referer,
},
"json": {"data":[prompt,1234,5,1],"event_data":None,"fn_index":3,"trigger_id":20,"session_hash":conversation.session_hash},
@@ -82,33 +82,14 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
method = "image"
prompt = format_prompt(messages) if prompt is None and conversation is None else prompt
- prompt = messages[-1]["content"] if prompt is None else prompt
+ prompt = format_image_prompt(messages, prompt)
session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
async with StreamSession(proxy=proxy, impersonate="chrome") as session:
session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
- user_uuid = None if conversation is None else getattr(conversation, "user_uuid", None)
- zerogpu_token = "[object Object]"
-
- cookies = get_cookies("huggingface.co", raise_requirements_error=False) if cookies is None else cookies
- if cookies:
- # Get current UTC time + 10 minutes
- dt = (datetime.now(timezone.utc) + timedelta(minutes=10)).isoformat(timespec='milliseconds')
- encoded_dt = urllib.parse.quote(dt)
- async with session.get(f"https://huggingface.co/api/spaces/deepseek-ai/Janus-Pro-7B/jwt?expiration={encoded_dt}&include_pro_status=true", cookies=cookies) as response:
- zerogpu_token = (await response.json())
- zerogpu_token = zerogpu_token["token"]
- if user_uuid is None:
- async with session.get(cls.url, cookies=cookies) as response:
- match = re.search(r""token":"([^&]+?)"", await response.text())
- if match:
- zerogpu_token = match.group(1)
- match = re.search(r""sessionUuid":"([^&]+?)"", await response.text())
- if match:
- user_uuid = match.group(1)
-
+ zerogpu_uuid, zerogpu_token = await get_zerogpu_token(session, conversation, cookies)
if conversation is None or not hasattr(conversation, "session_hash"):
- conversation = JsonConversation(session_hash=session_hash, zerogpu_token=zerogpu_token, uuid=user_uuid)
+ conversation = JsonConversation(session_hash=session_hash, zerogpu_token=zerogpu_token, zerogpu_uuid=zerogpu_uuid)
conversation.zerogpu_token = zerogpu_token
if return_conversation:
yield conversation
@@ -124,7 +105,7 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
try:
json_data = json.loads(decoded_line[6:])
if json_data.get('msg') == 'log':
- debug.log(json_data["log"])
+ yield Notification(json_data["log"])
if json_data.get('msg') == 'process_generating':
if 'output' in json_data and 'data' in json_data['output']:
@@ -141,4 +122,27 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
break
except json.JSONDecodeError:
- debug.log("Could not parse JSON:", decoded_line)
\ No newline at end of file
+ debug.log("Could not parse JSON:", decoded_line)
+
+async def get_zerogpu_token(session: StreamSession, conversation: JsonConversation, cookies: Cookies = None):
+ zerogpu_uuid = None if conversation is None else getattr(conversation, "zerogpu_uuid", None)
+ zerogpu_token = "[object Object]"
+
+ cookies = get_cookies("huggingface.co", raise_requirements_error=False) if cookies is None else cookies
+ if zerogpu_uuid is None:
+ async with session.get(Janus_Pro_7B.url, cookies=cookies) as response:
+ match = re.search(r""token":"([^&]+?)"", await response.text())
+ if match:
+ zerogpu_token = match.group(1)
+ match = re.search(r""sessionUuid":"([^&]+?)"", await response.text())
+ if match:
+ zerogpu_uuid = match.group(1)
+ if cookies:
+ # Get current UTC time + 10 minutes
+ dt = (datetime.now(timezone.utc) + timedelta(minutes=10)).isoformat(timespec='milliseconds')
+ encoded_dt = urllib.parse.quote(dt)
+ async with session.get(f"https://huggingface.co/api/spaces/deepseek-ai/Janus-Pro-7B/jwt?expiration={encoded_dt}&include_pro_status=true", cookies=cookies) as response:
+ zerogpu_token = (await response.json())
+ zerogpu_token = zerogpu_token["token"]
+
+ return zerogpu_uuid, zerogpu_token
\ No newline at end of file
diff --git a/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py b/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
index 66a46f7a..f1a5091e 100644
--- a/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
+++ b/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
@@ -8,6 +8,7 @@ from ...typing import AsyncResult, Messages
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..helper import format_prompt
from ...providers.response import JsonConversation, Reasoning
+from ..helper import get_last_user_message
from ... import debug
class Qwen_Qwen_2_5M_Demo(AsyncGeneratorProvider, ProviderModelMixin):
@@ -41,7 +42,7 @@ class Qwen_Qwen_2_5M_Demo(AsyncGeneratorProvider, ProviderModelMixin):
if return_conversation:
yield JsonConversation(session_hash=session_hash)
- prompt = format_prompt(messages) if conversation is None else messages[-1]["content"]
+ prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages)
headers = {
'accept': '*/*',
diff --git a/g4f/Provider/hf_space/StableDiffusion35Large.py b/g4f/Provider/hf_space/StableDiffusion35Large.py
index e047181d..82d349d8 100644
--- a/g4f/Provider/hf_space/StableDiffusion35Large.py
+++ b/g4f/Provider/hf_space/StableDiffusion35Large.py
@@ -7,6 +7,7 @@ from ...typing import AsyncResult, Messages
from ...image import ImageResponse, ImagePreview
from ...errors import ResponseError
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
class StableDiffusion35Large(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://stabilityai-stable-diffusion-3-5-large.hf.space"
@@ -42,7 +43,7 @@ class StableDiffusion35Large(AsyncGeneratorProvider, ProviderModelMixin):
if api_key is not None:
headers["Authorization"] = f"Bearer {api_key}"
async with ClientSession(headers=headers) as session:
- prompt = messages[-1]["content"] if prompt is None else prompt
+ prompt = format_image_prompt(messages, prompt)
data = {
"data": [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
}
diff --git a/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py b/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
index d4a052f5..3496f718 100644
--- a/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
+++ b/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
@@ -7,6 +7,7 @@ from ...typing import AsyncResult, Messages
from ...image import ImageResponse
from ...errors import ResponseError
from ...requests.raise_for_status import raise_for_status
+from ..helper import format_image_prompt
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
class VoodoohopFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
@@ -37,10 +38,7 @@ class VoodoohopFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
) -> AsyncResult:
width = max(32, width - (width % 8))
height = max(32, height - (height % 8))
-
- if prompt is None:
- prompt = messages[-1]["content"]
-
+ prompt = format_image_prompt(messages, prompt)
payload = {
"data": [
prompt,
diff --git a/g4f/Provider/mini_max/HailuoAI.py b/g4f/Provider/mini_max/HailuoAI.py
index d8c145a1..9b5a8929 100644
--- a/g4f/Provider/mini_max/HailuoAI.py
+++ b/g4f/Provider/mini_max/HailuoAI.py
@@ -10,6 +10,7 @@ from ..base_provider import AsyncAuthedProvider, ProviderModelMixin, format_prom
from ..mini_max.crypt import CallbackResults, get_browser_callback, generate_yy_header, get_body_to_yy
from ...requests import get_args_from_nodriver, raise_for_status
from ...providers.response import AuthResult, JsonConversation, RequestLogin, TitleGeneration
+from ..helper import get_last_user_message
from ... import debug
class Conversation(JsonConversation):
@@ -62,7 +63,7 @@ class HailuoAI(AsyncAuthedProvider, ProviderModelMixin):
conversation = None
form_data = {
"characterID": 1 if conversation is None else getattr(conversation, "characterID", 1),
- "msgContent": format_prompt(messages) if conversation is None else messages[-1]["content"],
+ "msgContent": format_prompt(messages) if conversation is None else get_last_user_message(messages),
"chatID": 0 if conversation is None else getattr(conversation, "chatID", 0),
"searchMode": 0
}
diff --git a/g4f/Provider/needs_auth/Anthropic.py b/g4f/Provider/needs_auth/Anthropic.py
index cdc8f680..6e356c9e 100644
--- a/g4f/Provider/needs_auth/Anthropic.py
+++ b/g4f/Provider/needs_auth/Anthropic.py
@@ -98,7 +98,7 @@ class Anthropic(OpenaiAPI):
"text": messages[-1]["content"]
}
]
- system = "\n".join([message for message in messages if message.get("role") == "system"])
+ system = "\n".join([message["content"] for message in messages if message.get("role") == "system"])
if system:
messages = [message for message in messages if message.get("role") != "system"]
else:
diff --git a/g4f/Provider/needs_auth/BingCreateImages.py b/g4f/Provider/needs_auth/BingCreateImages.py
index c2d403d7..b198d201 100644
--- a/g4f/Provider/needs_auth/BingCreateImages.py
+++ b/g4f/Provider/needs_auth/BingCreateImages.py
@@ -6,6 +6,7 @@ from ...errors import MissingAuthError
from ...typing import AsyncResult, Messages, Cookies
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..bing.create_images import create_images, create_session
+from ..helper import format_image_prompt
class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
label = "Microsoft Designer in Bing"
@@ -35,7 +36,7 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
**kwargs
) -> AsyncResult:
session = BingCreateImages(cookies, proxy, api_key)
- yield await session.generate(messages[-1]["content"] if prompt is None else prompt)
+ yield await session.generate(format_image_prompt(messages, prompt))
async def generate(self, prompt: str) -> ImageResponse:
"""
diff --git a/g4f/Provider/needs_auth/DeepInfra.py b/g4f/Provider/needs_auth/DeepInfra.py
index a5f6350a..b1d7c1d2 100644
--- a/g4f/Provider/needs_auth/DeepInfra.py
+++ b/g4f/Provider/needs_auth/DeepInfra.py
@@ -5,6 +5,7 @@ from ...typing import AsyncResult, Messages
from ...requests import StreamSession, raise_for_status
from ...image import ImageResponse
from ..template import OpenaiTemplate
+from ..helper import format_image_prompt
class DeepInfra(OpenaiTemplate):
url = "https://deepinfra.com"
@@ -55,7 +56,7 @@ class DeepInfra(OpenaiTemplate):
) -> AsyncResult:
if model in cls.get_image_models():
yield cls.create_async_image(
- messages[-1]["content"] if prompt is None else prompt,
+ format_image_prompt(messages, prompt),
model,
**kwargs
)
diff --git a/g4f/Provider/needs_auth/DeepSeekAPI.py b/g4f/Provider/needs_auth/DeepSeekAPI.py
index ad7f32fe..365ac92e 100644
--- a/g4f/Provider/needs_auth/DeepSeekAPI.py
+++ b/g4f/Provider/needs_auth/DeepSeekAPI.py
@@ -31,7 +31,7 @@ try:
challenge = self._get_pow_challenge()
pow_response = self.pow_solver.solve_challenge(challenge)
headers = self._get_headers(pow_response)
-
+
response = requests.request(
method=method,
url=url,
diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py
index e6d71255..8c555e85 100644
--- a/g4f/Provider/needs_auth/Gemini.py
+++ b/g4f/Provider/needs_auth/Gemini.py
@@ -25,6 +25,7 @@ from ...requests.aiohttp import get_connector
from ...requests import get_nodriver
from ...errors import MissingAuthError
from ...image import ImageResponse, to_bytes
+from ..helper import get_last_user_message
from ... import debug
REQUEST_HEADERS = {
@@ -78,7 +79,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
if debug.logging:
print("Skip nodriver login in Gemini provider")
return
- browser = await get_nodriver(proxy=proxy, user_data_dir="gemini")
+ browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="gemini")
try:
login_url = os.environ.get("G4F_LOGIN_URL")
if login_url:
@@ -91,7 +92,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
await page.close()
cls._cookies = cookies
finally:
- browser.stop()
+ stop_browser()
@classmethod
async def create_async_generator(
@@ -107,7 +108,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
language: str = "en",
**kwargs
) -> AsyncResult:
- prompt = format_prompt(messages) if conversation is None else messages[-1]["content"]
+ prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages)
cls._cookies = cookies or cls._cookies or get_cookies(".google.com", False, True)
base_connector = get_connector(connector, proxy)
diff --git a/g4f/Provider/needs_auth/GithubCopilot.py b/g4f/Provider/needs_auth/GithubCopilot.py
index 1deca50c..4b9db8e9 100644
--- a/g4f/Provider/needs_auth/GithubCopilot.py
+++ b/g4f/Provider/needs_auth/GithubCopilot.py
@@ -7,7 +7,7 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, BaseConv
from ...typing import AsyncResult, Messages, Cookies
from ...requests.raise_for_status import raise_for_status
from ...requests.aiohttp import get_connector
-from ...providers.helper import format_prompt
+from ...providers.helper import format_prompt, get_last_user_message
from ...cookies import get_cookies
class Conversation(BaseConversation):
@@ -78,7 +78,7 @@ class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin):
conversation_id = (await response.json()).get("thread_id")
if return_conversation:
yield Conversation(conversation_id)
- content = messages[-1]["content"]
+ content = get_last_user_message(messages)
else:
content = format_prompt(messages)
json_data = {
diff --git a/g4f/Provider/needs_auth/HuggingChat.py b/g4f/Provider/needs_auth/HuggingChat.py
deleted file mode 100644
index 316a8317..00000000
--- a/g4f/Provider/needs_auth/HuggingChat.py
+++ /dev/null
@@ -1,265 +0,0 @@
-from __future__ import annotations
-
-import json
-import re
-import os
-import requests
-import base64
-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, AsyncAuthedProvider, AuthResult
-from ..helper import format_prompt
-from ...typing import AsyncResult, Messages, Cookies, ImagesType
-from ...errors import MissingRequirementsError, MissingAuthError, ResponseError
-from ...image import to_bytes
-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, RequestLogin
-from ...cookies import get_cookies
-from ... import debug
-
-class Conversation(JsonConversation):
- def __init__(self, models: dict):
- self.models: dict = models
-
-class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
- url = "https://huggingface.co/chat"
-
- working = True
- use_nodriver = True
- supports_stream = True
- needs_auth = True
-
- default_model = "Qwen/Qwen2.5-72B-Instruct"
- default_image_model = "black-forest-labs/FLUX.1-dev"
- image_models = [
- default_image_model,
- "black-forest-labs/FLUX.1-schnell",
- ]
- fallback_models = [
- default_model,
- 'meta-llama/Llama-3.3-70B-Instruct',
- 'CohereForAI/c4ai-command-r-plus-08-2024',
- 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
- 'Qwen/QwQ-32B-Preview',
- 'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
- 'Qwen/Qwen2.5-Coder-32B-Instruct',
- 'meta-llama/Llama-3.2-11B-Vision-Instruct',
- 'mistralai/Mistral-Nemo-Instruct-2407',
- 'microsoft/Phi-3.5-mini-instruct',
- ] + image_models
- model_aliases = {
- ### Chat ###
- "qwen-2.5-72b": "Qwen/Qwen2.5-Coder-32B-Instruct",
- "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct",
- "command-r-plus": "CohereForAI/c4ai-command-r-plus-08-2024",
- "deepseek-r1": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
- "qwq-32b": "Qwen/QwQ-32B-Preview",
- "nemotron-70b": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
- "qwen-2.5-coder-32b": "Qwen/Qwen2.5-Coder-32B-Instruct",
- "llama-3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct",
- "mistral-nemo": "mistralai/Mistral-Nemo-Instruct-2407",
- "phi-3.5-mini": "microsoft/Phi-3.5-mini-instruct",
- ### Image ###
- "flux-dev": "black-forest-labs/FLUX.1-dev",
- "flux-schnell": "black-forest-labs/FLUX.1-schnell",
- ### Used in other providers ###
- "qwen-2-vl-7b": "Qwen/Qwen2-VL-7B-Instruct",
- "gemma-2-27b": "google/gemma-2-27b-it",
- "qwen-2-72b": "Qwen/Qwen2-72B-Instruct",
- "qvq-72b": "Qwen/QVQ-72B-Preview",
- "sd-3.5": "stabilityai/stable-diffusion-3.5-large",
- }
-
- @classmethod
- def get_models(cls):
- if not cls.models:
- try:
- text = requests.get(cls.url).text
- text = re.sub(r',parameters:{[^}]+?}', '', text)
- text = re.search(r'models:(\[.+?\]),oldModels:', text).group(1)
- text = text.replace('void 0', 'null')
- def add_quotation_mark(match):
- return f'{match.group(1)}"{match.group(2)}":'
- text = re.sub(r'([{,])([A-Za-z0-9_]+?):', add_quotation_mark, text)
- models = json.loads(text)
- cls.text_models = [model["id"] for model in models]
- cls.models = cls.text_models + cls.image_models
- cls.vision_models = [model["id"] for model in models if model["multimodal"]]
- except Exception as e:
- debug.log(f"HuggingChat: Error reading models: {type(e).__name__}: {e}")
- cls.models = [*cls.fallback_models]
- return cls.models
-
- @classmethod
- async def on_auth_async(cls, cookies: Cookies = None, proxy: str = None, **kwargs) -> AsyncIterator:
- if cookies is None:
- cookies = get_cookies("huggingface.co", single_browser=True)
- 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,
- auth_result: AuthResult,
- prompt: str = None,
- images: ImagesType = None,
- return_conversation: bool = False,
- conversation: Conversation = None,
- web_search: bool = False,
- **kwargs
- ) -> AsyncResult:
- if not has_curl_cffi:
- raise MissingRequirementsError('Install "curl_cffi" package | pip install -U curl_cffi')
- model = cls.get_model(model)
-
- session = Session(**auth_result.get_dict())
-
- if conversation is None or not hasattr(conversation, "models"):
- conversation = Conversation({})
-
- if model not in conversation.models:
- conversationId = cls.create_conversation(session, model)
- messageId = cls.fetch_message_id(session, conversationId)
- conversation.models[model] = {"conversationId": conversationId, "messageId": messageId}
- if return_conversation:
- yield conversation
- inputs = format_prompt(messages)
- else:
- conversationId = conversation.models[model]["conversationId"]
- conversation.models[model]["messageId"] = cls.fetch_message_id(session, conversationId)
- inputs = messages[-1]["content"]
-
- debug.log(f"Use: {json.dumps(conversation.models[model])}")
-
- settings = {
- "inputs": inputs,
- "id": conversation.models[model]["messageId"],
- "is_retry": False,
- "is_continue": False,
- "web_search": web_search,
- "tools": ["000000000000000000000001"] if model in cls.image_models else [],
- }
-
- headers = {
- 'accept': '*/*',
- 'origin': 'https://huggingface.co',
- 'referer': f'https://huggingface.co/chat/conversation/{conversationId}',
- }
- data = CurlMime()
- data.addpart('data', data=json.dumps(settings, separators=(',', ':')))
- if images is not None:
- for image, filename in images:
- data.addpart(
- "files",
- filename=f"base64;{filename}",
- data=base64.b64encode(to_bytes(image))
- )
-
- response = session.post(
- f'https://huggingface.co/chat/conversation/{conversationId}',
- headers=headers,
- multipart=data,
- stream=True
- )
- raise_for_status(response)
-
- sources = None
- for line in response.iter_lines():
- if not line:
- continue
- try:
- line = json.loads(line)
- except json.JSONDecodeError as e:
- debug.log(f"Failed to decode JSON: {line}, error: {e}")
- continue
- if "type" not in line:
- raise RuntimeError(f"Response: {line}")
- elif line["type"] == "stream":
- yield line["token"].replace('\u0000', '')
- elif line["type"] == "finalAnswer":
- break
- elif line["type"] == "file":
- url = f"https://huggingface.co/chat/conversation/{conversationId}/output/{line['sha']}"
- prompt = messages[-1]["content"] if prompt is None else prompt
- yield ImageResponse(url, alt=prompt, options={"cookies": auth_result.cookies})
- elif line["type"] == "webSearch" and "sources" in line:
- sources = Sources(line["sources"])
- elif line["type"] == "title":
- yield TitleGeneration(line["title"])
- elif line["type"] == "reasoning":
- yield Reasoning(line.get("token"), line.get("status"))
-
- if sources is not None:
- yield sources
-
- @classmethod
- def create_conversation(cls, session: Session, model: str):
- if model in cls.image_models:
- model = cls.default_model
- json_data = {
- '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
- 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")
-
- 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]]
- return data[message_keys["id"]]
-
- except (KeyError, IndexError, TypeError) as e:
- raise RuntimeError(f"Failed to extract message ID: {str(e)}")
diff --git a/g4f/Provider/needs_auth/HuggingFace.py b/g4f/Provider/needs_auth/HuggingFace.py
deleted file mode 100644
index 07e110b3..00000000
--- a/g4f/Provider/needs_auth/HuggingFace.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from __future__ import annotations
-
-import json
-import base64
-import random
-import requests
-
-from ...typing import AsyncResult, Messages
-from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_prompt
-from ...errors import ModelNotFoundError, ModelNotSupportedError, ResponseError
-from ...requests import StreamSession, raise_for_status
-from ...providers.response import FinishReason
-from ...image import ImageResponse
-from ... import debug
-
-from .HuggingChat import HuggingChat
-
-class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
- url = "https://huggingface.co"
- login_url = "https://huggingface.co/settings/tokens"
- working = True
- supports_message_history = True
- default_model = HuggingChat.default_model
- default_image_model = HuggingChat.default_image_model
- model_aliases = HuggingChat.model_aliases
- extra_models = [
- "meta-llama/Llama-3.2-11B-Vision-Instruct",
- "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
- "NousResearch/Hermes-3-Llama-3.1-8B",
- ]
-
- @classmethod
- def get_models(cls) -> list[str]:
- if not cls.models:
- url = "https://huggingface.co/api/models?inference=warm&pipeline_tag=text-generation"
- models = [model["id"] for model in requests.get(url).json()]
- models.extend(cls.extra_models)
- models.sort()
- if not cls.image_models:
- url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
- cls.image_models = [model["id"] for model in requests.get(url).json() if model["trendingScore"] >= 20]
- cls.image_models.sort()
- models.extend(cls.image_models)
- cls.models = list(set(models))
- return cls.models
-
- @classmethod
- async def create_async_generator(
- cls,
- model: str,
- messages: Messages,
- stream: bool = True,
- proxy: str = None,
- api_base: str = "https://api-inference.huggingface.co",
- api_key: str = None,
- max_tokens: int = 1024,
- temperature: float = None,
- prompt: str = None,
- action: str = None,
- extra_data: dict = {},
- **kwargs
- ) -> AsyncResult:
- try:
- model = cls.get_model(model)
- except ModelNotSupportedError:
- pass
- 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',
- }
- if api_key is not None:
- headers["Authorization"] = f"Bearer {api_key}"
- payload = None
- if cls.get_models() and model in cls.image_models:
- stream = False
- prompt = messages[-1]["content"] if prompt is None else prompt
- payload = {"inputs": prompt, "parameters": {"seed": random.randint(0, 2**32), **extra_data}}
- else:
- params = {
- "return_full_text": False,
- "max_new_tokens": max_tokens,
- "temperature": temperature,
- **extra_data
- }
- do_continue = action == "continue"
- async with StreamSession(
- headers=headers,
- proxy=proxy,
- timeout=600
- ) as session:
- if payload is None:
- async with session.get(f"https://huggingface.co/api/models/{model}") as response:
- if response.status == 404:
- raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__}")
- await raise_for_status(response)
- model_data = await response.json()
- model_type = None
- if "config" in model_data and "model_type" in model_data["config"]:
- model_type = model_data["config"]["model_type"]
- debug.log(f"Model type: {model_type}")
- inputs = get_inputs(messages, model_data, model_type, do_continue)
- debug.log(f"Inputs len: {len(inputs)}")
- if len(inputs) > 4096:
- if len(messages) > 6:
- messages = messages[:3] + messages[-3:]
- else:
- messages = [m for m in messages if m["role"] == "system"] + [messages[-1]]
- inputs = get_inputs(messages, model_data, model_type, do_continue)
- debug.log(f"New len: {len(inputs)}")
- if model_type == "gpt2" and max_tokens >= 1024:
- params["max_new_tokens"] = 512
- payload = {"inputs": inputs, "parameters": params, "stream": stream}
-
- async with session.post(f"{api_base.rstrip('/')}/models/{model}", json=payload) as response:
- if response.status == 404:
- raise ModelNotFoundError(f"Model is not supported: {model}")
- await raise_for_status(response)
- if stream:
- first = True
- is_special = False
- async for line in response.iter_lines():
- if line.startswith(b"data:"):
- data = json.loads(line[5:])
- if "error" in data:
- raise ResponseError(data["error"])
- if not data["token"]["special"]:
- chunk = data["token"]["text"]
- if first and not do_continue:
- first = False
- chunk = chunk.lstrip()
- if chunk:
- yield chunk
- else:
- is_special = True
- debug.log(f"Special token: {is_special}")
- yield FinishReason("stop" if is_special else "length")
- else:
- if response.headers["content-type"].startswith("image/"):
- base64_data = base64.b64encode(b"".join([chunk async for chunk in response.iter_content()]))
- url = f"data:{response.headers['content-type']};base64,{base64_data.decode()}"
- yield ImageResponse(url, prompt)
- else:
- yield (await response.json())[0]["generated_text"].strip()
-
-def format_prompt_mistral(messages: Messages, do_continue: bool = False) -> str:
- system_messages = [message["content"] for message in messages if message["role"] == "system"]
- question = " ".join([messages[-1]["content"], *system_messages])
- history = "\n".join([
- f"[INST]{messages[idx-1]['content']} [/INST] {message['content']}"
- for idx, message in enumerate(messages)
- if message["role"] == "assistant"
- ])
- if do_continue:
- return history[:-len('')]
- return f"{history}\n[INST] {question} [/INST]"
-
-def format_prompt_qwen(messages: Messages, do_continue: bool = False) -> str:
- prompt = "".join([
- f"<|im_start|>{message['role']}\n{message['content']}\n<|im_end|>\n" for message in messages
- ]) + ("" if do_continue else "<|im_start|>assistant\n")
- if do_continue:
- return prompt[:-len("\n<|im_end|>\n")]
- return prompt
-
-def format_prompt_qwen2(messages: Messages, do_continue: bool = False) -> str:
- prompt = "".join([
- f"\u003C|{message['role'].capitalize()}|\u003E{message['content']}\u003C|end▁of▁sentence|\u003E" for message in messages
- ]) + ("" if do_continue else "\u003C|Assistant|\u003E")
- if do_continue:
- return prompt[:-len("\u003C|Assistant|\u003E")]
- return prompt
-
-def format_prompt_llama(messages: Messages, do_continue: bool = False) -> str:
- prompt = "<|begin_of_text|>" + "".join([
- f"<|start_header_id|>{message['role']}<|end_header_id|>\n\n{message['content']}\n<|eot_id|>\n" for message in messages
- ]) + ("" if do_continue else "<|start_header_id|>assistant<|end_header_id|>\n\n")
- if do_continue:
- return prompt[:-len("\n<|eot_id|>\n")]
- return prompt
-
-def format_prompt_custom(messages: Messages, end_token: str = "", do_continue: bool = False) -> str:
- prompt = "".join([
- f"<|{message['role']}|>\n{message['content']}{end_token}\n" for message in messages
- ]) + ("" if do_continue else "<|assistant|>\n")
- if do_continue:
- return prompt[:-len(end_token + "\n")]
- return prompt
-
-def get_inputs(messages: Messages, model_data: dict, model_type: str, do_continue: bool = False) -> str:
- if model_type in ("gpt2", "gpt_neo", "gemma", "gemma2"):
- inputs = format_prompt(messages, do_continue=do_continue)
- elif model_type == "mistral" and model_data.get("author") == "mistralai":
- inputs = format_prompt_mistral(messages, do_continue)
- elif "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
- eos_token = model_data["config"]["tokenizer_config"]["eos_token"]
- if eos_token in ("<|endoftext|>", "", ""):
- inputs = format_prompt_custom(messages, eos_token, do_continue)
- elif eos_token == "<|im_end|>":
- inputs = format_prompt_qwen(messages, do_continue)
- elif "content" in eos_token and eos_token["content"] == "\u003C|end▁of▁sentence|\u003E":
- inputs = format_prompt_qwen2(messages, do_continue)
- elif eos_token == "<|eot_id|>":
- inputs = format_prompt_llama(messages, do_continue)
- else:
- inputs = format_prompt(messages, do_continue=do_continue)
- else:
- inputs = format_prompt(messages, do_continue=do_continue)
- return inputs
\ No newline at end of file
diff --git a/g4f/Provider/needs_auth/HuggingFaceAPI.py b/g4f/Provider/needs_auth/HuggingFaceAPI.py
deleted file mode 100644
index c9a15260..00000000
--- a/g4f/Provider/needs_auth/HuggingFaceAPI.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from __future__ import annotations
-
-from ..template import OpenaiTemplate
-from .HuggingChat import HuggingChat
-from ...providers.types import Messages
-from ... import debug
-
-class HuggingFaceAPI(OpenaiTemplate):
- label = "HuggingFace (Inference API)"
- parent = "HuggingFace"
- url = "https://api-inference.huggingface.com"
- api_base = "https://api-inference.huggingface.co/v1"
- working = True
- needs_auth = True
-
- default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
- default_vision_model = default_model
- vision_models = [default_vision_model, "Qwen/Qwen2-VL-7B-Instruct"]
- model_aliases = HuggingChat.model_aliases
-
- @classmethod
- def get_models(cls, **kwargs):
- if not cls.models:
- HuggingChat.get_models()
- cls.models = list(set(HuggingChat.text_models + cls.vision_models))
- return cls.models
-
- @classmethod
- async def create_async_generator(
- cls,
- model: str,
- messages: Messages,
- api_base: str = None,
- max_tokens: int = 2048,
- max_inputs_lenght: int = 10000,
- **kwargs
- ):
- if api_base is None:
- model_name = model
- if model in cls.model_aliases:
- model_name = cls.model_aliases[model]
- api_base = f"https://api-inference.huggingface.co/models/{model_name}/v1"
- start = calculate_lenght(messages)
- if start > max_inputs_lenght:
- if len(messages) > 6:
- messages = messages[:3] + messages[-3:]
- if calculate_lenght(messages) > max_inputs_lenght:
- if len(messages) > 2:
- messages = [m for m in messages if m["role"] == "system"] + messages[-1:]
- if len(messages) > 1 and calculate_lenght(messages) > max_inputs_lenght:
- messages = [messages[-1]]
- debug.log(f"Messages trimmed from: {start} to: {calculate_lenght(messages)}")
- async for chunk in super().create_async_generator(model, messages, api_base=api_base, max_tokens=max_tokens, **kwargs):
- yield chunk
-
-def calculate_lenght(messages: Messages) -> int:
- return sum([len(message["content"]) + 16 for message in messages])
\ No newline at end of file
diff --git a/g4f/Provider/needs_auth/MicrosoftDesigner.py b/g4f/Provider/needs_auth/MicrosoftDesigner.py
index c413c19b..3ce8e618 100644
--- a/g4f/Provider/needs_auth/MicrosoftDesigner.py
+++ b/g4f/Provider/needs_auth/MicrosoftDesigner.py
@@ -14,7 +14,7 @@ from ...requests.aiohttp import get_connector
from ...requests import get_nodriver
from ..Copilot import get_headers, get_har_files
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import get_random_hex
+from ..helper import get_random_hex, format_image_prompt
from ... import debug
class MicrosoftDesigner(AsyncGeneratorProvider, ProviderModelMixin):
@@ -39,7 +39,7 @@ class MicrosoftDesigner(AsyncGeneratorProvider, ProviderModelMixin):
image_size = "1024x1024"
if model != cls.default_image_model and model in cls.image_models:
image_size = model
- yield await cls.generate(messages[-1]["content"] if prompt is None else prompt, image_size, proxy)
+ yield await cls.generate(format_image_prompt(messages, prompt), image_size, proxy)
@classmethod
async def generate(cls, prompt: str, image_size: str, proxy: str = None) -> ImageResponse:
@@ -143,7 +143,7 @@ def readHAR(url: str) -> tuple[str, str]:
return api_key, user_agent
async def get_access_token_and_user_agent(url: str, proxy: str = None):
- browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
+ browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
try:
page = await browser.get(url)
user_agent = await page.evaluate("navigator.userAgent")
@@ -168,4 +168,4 @@ async def get_access_token_and_user_agent(url: str, proxy: str = None):
await page.close()
return access_token, user_agent
finally:
- browser.stop()
\ No newline at end of file
+ stop_browser()
\ No newline at end of file
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index adcecb3f..8fde1090 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -98,7 +98,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
default_model = "auto"
default_image_model = "dall-e-3"
image_models = [default_image_model]
- text_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "gpt-4o-canmore", "o1", "o1-preview", "o1-mini"]
+ text_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "o1", "o1-preview", "o1-mini"]
vision_models = text_models
models = text_models + image_models
synthesize_content_type = "audio/mpeg"
@@ -598,7 +598,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
@classmethod
async def nodriver_auth(cls, proxy: str = None):
- browser = await get_nodriver(proxy=proxy)
+ browser, stop_browser = await get_nodriver(proxy=proxy)
try:
page = browser.main_tab
def on_request(event: nodriver.cdp.network.RequestWillBeSent):
@@ -648,7 +648,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
cls._create_request_args(RequestConfig.cookies, RequestConfig.headers, user_agent=user_agent)
cls._set_api_key(cls._api_key)
finally:
- browser.stop()
+ stop_browser()
@staticmethod
def get_default_headers() -> Dict[str, str]:
diff --git a/g4f/Provider/needs_auth/__init__.py b/g4f/Provider/needs_auth/__init__.py
index 691bc5be..e00e3b1f 100644
--- a/g4f/Provider/needs_auth/__init__.py
+++ b/g4f/Provider/needs_auth/__init__.py
@@ -13,9 +13,6 @@ from .GigaChat import GigaChat
from .GithubCopilot import GithubCopilot
from .GlhfChat import GlhfChat
from .Groq import Groq
-from .HuggingChat import HuggingChat
-from .HuggingFace import HuggingFace
-from .HuggingFaceAPI import HuggingFaceAPI
from .MetaAI import MetaAI
from .MetaAIAccount import MetaAIAccount
from .MicrosoftDesigner import MicrosoftDesigner
diff --git a/g4f/Provider/template/BackendApi.py b/g4f/Provider/template/BackendApi.py
index caf28219..8ef2ee4b 100644
--- a/g4f/Provider/template/BackendApi.py
+++ b/g4f/Provider/template/BackendApi.py
@@ -8,11 +8,11 @@ from urllib.parse import quote_plus
from ...typing import Messages, AsyncResult
from ...requests import StreamSession
from ...providers.base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ...providers.response import ProviderInfo, JsonConversation, PreviewResponse, SynthesizeData, TitleGeneration, RequestLogin
-from ...providers.response import Parameters, FinishReason, Usage, Reasoning
+from ...providers.response import *
+from ...image import get_image_extension
from ...errors import ModelNotSupportedError
from ..needs_auth.OpenaiAccount import OpenaiAccount
-from ..needs_auth.HuggingChat import HuggingChat
+from ..hf.HuggingChat import HuggingChat
from ... import debug
class BackendApi(AsyncGeneratorProvider, ProviderModelMixin):
@@ -98,8 +98,7 @@ class BackendApi(AsyncGeneratorProvider, ProviderModelMixin):
yield PreviewResponse(data[data_type])
elif data_type == "content":
def on_image(match):
- extension = match.group(3).split(".")[-1].split("?")[0]
- extension = "" if not extension or len(extension) > 4 else f".{extension}"
+ extension = get_image_extension(match.group(3))
filename = f"{int(time.time())}_{quote_plus(match.group(1)[:100], '')}{extension}"
download_url = f"/download/{filename}?url={cls.url}{match.group(3)}"
return f"[](/images/{filename})"
@@ -119,6 +118,6 @@ class BackendApi(AsyncGeneratorProvider, ProviderModelMixin):
elif data_type == "finish":
yield FinishReason(data[data_type]["reason"])
elif data_type == "log":
- debug.log(data[data_type])
+ yield DebugResponse.from_dict(data[data_type])
else:
- debug.log(f"Unknown data: ({data_type}) {data}")
\ No newline at end of file
+ yield DebugResponse.from_dict(data)
diff --git a/g4f/Provider/template/OpenaiTemplate.py b/g4f/Provider/template/OpenaiTemplate.py
index 62a44c55..3bf8c104 100644
--- a/g4f/Provider/template/OpenaiTemplate.py
+++ b/g4f/Provider/template/OpenaiTemplate.py
@@ -4,11 +4,11 @@ import json
import time
import requests
-from ..helper import filter_none
+from ..helper import filter_none, format_image_prompt
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
from ...typing import Union, Optional, AsyncResult, Messages, ImagesType
from ...requests import StreamSession, raise_for_status
-from ...providers.response import FinishReason, ToolCalls, Usage, Reasoning, ImageResponse
+from ...providers.response import FinishReason, ToolCalls, Usage, ImageResponse
from ...errors import MissingAuthError, ResponseError
from ...image import to_data_uri
from ... import debug
@@ -82,7 +82,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
# Proxy for image generation feature
if model and model in cls.image_models:
data = {
- "prompt": messages[-1]["content"] if prompt is None else prompt,
+ "prompt": format_image_prompt(messages, prompt),
"model": model,
}
async with session.post(f"{api_base.rstrip('/')}/images/generations", json=data, ssl=cls.ssl) as response:
@@ -154,17 +154,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
delta = delta.lstrip()
if delta:
first = False
- if is_thinking:
- if "" in delta:
- yield Reasoning(None, f"Finished in {round(time.time()-is_thinking, 2)} seconds")
- is_thinking = 0
- else:
- yield Reasoning(delta)
- elif "" in delta:
- is_thinking = time.time()
- yield Reasoning(None, "Is thinking...")
- else:
- yield delta
+ yield delta
if "usage" in data and data["usage"]:
yield Usage(**data["usage"])
if "finish_reason" in choice and choice["finish_reason"] is not None:
--
cgit v1.2.3