diff --git a/g4f/Provider/Jmuz.py b/g4f/Provider/Jmuz.py index 0615310e..95ea2b89 100644 --- a/g4f/Provider/Jmuz.py +++ b/g4f/Provider/Jmuz.py @@ -5,6 +5,7 @@ from .needs_auth.OpenaiAPI import OpenaiAPI class Jmuz(OpenaiAPI): label = "Jmuz" + login_url = None api_base = "https://jmuz.me/gpt/api/v2" api_key = "prod" @@ -33,6 +34,8 @@ class Jmuz(OpenaiAPI): model: str, messages: Messages, stream: bool = False, + aoi_key: str = None, + api_base: str = None, **kwargs ) -> AsyncResult: model = cls.get_model(model) diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py index 4990a869..d4b46ea0 100644 --- a/g4f/Provider/PollinationsAI.py +++ b/g4f/Provider/PollinationsAI.py @@ -170,13 +170,13 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): params = {k: v for k, v in params.items() if v is not None} async with ClientSession(headers=headers) as session: - prompt = quote(messages[-1]["content"] if prompt is None else prompt) + prompt = messages[-1]["content"] if prompt is None else prompt param_string = "&".join(f"{k}={v}" for k, v in params.items()) - url = f"{cls.image_api_endpoint}/prompt/{prompt}?{param_string}" + url = f"{cls.image_api_endpoint}/prompt/{quote(prompt)}?{param_string}" async with session.head(url, proxy=proxy) as response: if response.status == 200: - image_response = ImageResponse(images=url, alt=messages[-1]["content"] if prompt is None else prompt) + image_response = ImageResponse(images=url, alt=prompt) yield image_response @classmethod @@ -225,4 +225,4 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): content = json_response['choices'][0]['message']['content'] yield content except json.JSONDecodeError: - yield decoded_chunk + pass diff --git a/g4f/Provider/hf_space/Qwen_QVQ_72B.py b/g4f/Provider/hf_space/Qwen_QVQ_72B.py new file mode 100644 index 00000000..853b9770 --- /dev/null +++ b/g4f/Provider/hf_space/Qwen_QVQ_72B.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json +from aiohttp import ClientSession, FormData + +from ...typing import AsyncResult, Messages, ImagesType +from ...requests import raise_for_status +from ...errors import ResponseError +from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin +from ..helper import format_prompt, get_random_string +from ...image import to_bytes, is_accepted_format + +class Qwen_QVQ_72B(AsyncGeneratorProvider, ProviderModelMixin): + url = "https://qwen-qvq-72b-preview.hf.space" + api_endpoint = "/gradio_api/call/generate" + + working = True + + default_model = "Qwen/QwQ-32B-Preview" + models = [default_model] + + @classmethod + async def create_async_generator( + cls, model: str, messages: Messages, + images: ImagesType = None, + api_key: str = None, + proxy: str = None, + **kwargs + ) -> AsyncResult: + headers = { + "Accept": "application/json", + } + if api_key is not None: + headers["Authorization"] = f"Bearer {api_key}" + async with ClientSession(headers=headers) as session: + if images: + data = FormData() + data_bytes = to_bytes(images[0][0]) + data.add_field("files", data_bytes, content_type=is_accepted_format(data_bytes), filename=images[0][1]) + url = f"https://qwen-qvq-72b-preview.hf.space/gradio_api/upload?upload_id={get_random_string()}" + async with session.post(url, data=data, proxy=proxy) as response: + await raise_for_status(response) + image = await response.json() + data = {"data": [{"path": image[0]}, format_prompt(messages)]} + else: + data = {"data": [None, format_prompt(messages)]} + async with session.post(f"{cls.url}{cls.api_endpoint}", json=data, proxy=proxy) as response: + await raise_for_status(response) + event_id = (await response.json()).get("event_id") + async with session.get(f"{cls.url}{cls.api_endpoint}/{event_id}") as event_response: + await raise_for_status(event_response) + event = None + text_position = 0 + async for chunk in event_response.content: + if chunk.startswith(b"event: "): + event = chunk[7:].decode(errors="replace").strip() + if chunk.startswith(b"data: "): + if event == "error": + raise ResponseError(f"GPU token limit exceeded: {chunk.decode(errors='replace')}") + if event in ("complete", "generating"): + try: + data = json.loads(chunk[6:]) + except (json.JSONDecodeError, KeyError, TypeError) as e: + raise RuntimeError(f"Failed to read response: {chunk.decode(errors='replace')}", e) + if event == "generating": + if isinstance(data[0], str): + yield data[0][text_position:] + text_position = len(data[0]) + else: + break \ No newline at end of file diff --git a/g4f/Provider/hf_space/StableDiffusion35Large.py b/g4f/Provider/hf_space/StableDiffusion35Large.py new file mode 100644 index 00000000..445ce7ea --- /dev/null +++ b/g4f/Provider/hf_space/StableDiffusion35Large.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +import json +from aiohttp import ClientSession + +from ...typing import AsyncResult, Messages +from ...image import ImageResponse, ImagePreview +from ...errors import ResponseError +from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin + +class StableDiffusion35Large(AsyncGeneratorProvider, ProviderModelMixin): + url = "https://stabilityai-stable-diffusion-3-5-large.hf.space" + api_endpoint = "/gradio_api/call/infer" + + working = True + + default_model = 'stable-diffusion-3.5-large' + models = [default_model] + image_models = [default_model] + + @classmethod + async def create_async_generator( + cls, model: str, messages: Messages, + prompt: str = None, + negative_prompt: str = None, + api_key: str = None, + proxy: str = None, + width: int = 1024, + height: int = 1024, + guidance_scale: float = 4.5, + num_inference_steps: int = 50, + seed: int = 0, + randomize_seed: bool = True, + **kwargs + ) -> AsyncResult: + headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + 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 + data = { + "data": [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps] + } + async with session.post(f"{cls.url}{cls.api_endpoint}", json=data, proxy=proxy) as response: + response.raise_for_status() + event_id = (await response.json()).get("event_id") + async with session.get(f"{cls.url}{cls.api_endpoint}/{event_id}") as event_response: + event_response.raise_for_status() + event = None + async for chunk in event_response.content: + if chunk.startswith(b"event: "): + event = chunk[7:].decode(errors="replace").strip() + if chunk.startswith(b"data: "): + if event == "error": + raise ResponseError(f"GPU token limit exceeded: {chunk.decode(errors='replace')}") + if event in ("complete", "generating"): + try: + data = json.loads(chunk[6:]) + if data is None: + continue + url = data[0]["url"] + except (json.JSONDecodeError, KeyError, TypeError) as e: + raise RuntimeError(f"Failed to parse image URL: {chunk.decode(errors='replace')}", e) + if event == "generating": + yield ImagePreview(url, prompt) + else: + yield ImageResponse(url, prompt) + break diff --git a/g4f/Provider/hf_space/__init__.py b/g4f/Provider/hf_space/__init__.py index 87dfb32b..5ab7ad22 100644 --- a/g4f/Provider/hf_space/__init__.py +++ b/g4f/Provider/hf_space/__init__.py @@ -1,18 +1,22 @@ from __future__ import annotations -from ...typing import AsyncResult, Messages +from ...typing import AsyncResult, Messages, ImagesType from ...errors import ResponseError from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin from .BlackForestLabsFlux1Dev import BlackForestLabsFlux1Dev from .BlackForestLabsFlux1Schnell import BlackForestLabsFlux1Schnell from .VoodoohopFlux1Schnell import VoodoohopFlux1Schnell +from .StableDiffusion35Large import StableDiffusion35Large +from .Qwen_QVQ_72B import Qwen_QVQ_72B class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin): url = "https://huggingface.co/spaces" + parent = "HuggingFace" working = True default_model = BlackForestLabsFlux1Dev.default_model - providers = [BlackForestLabsFlux1Dev, BlackForestLabsFlux1Schnell, VoodoohopFlux1Schnell] + default_vision_model = Qwen_QVQ_72B.default_model + providers = [BlackForestLabsFlux1Dev, BlackForestLabsFlux1Schnell, VoodoohopFlux1Schnell, StableDiffusion35Large, Qwen_QVQ_72B] @classmethod def get_parameters(cls, **kwargs) -> dict: @@ -33,8 +37,10 @@ class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin): @classmethod async def create_async_generator( - cls, model: str, messages: Messages, **kwargs + cls, model: str, messages: Messages, images: ImagesType = None, **kwargs ) -> AsyncResult: + if not model and images is not None: + model = cls.default_vision_model is_started = False for provider in cls.providers: if model in provider.model_aliases: diff --git a/g4f/Provider/needs_auth/HuggingFace.py b/g4f/Provider/needs_auth/HuggingFace.py index 80c0d97b..c15dc767 100644 --- a/g4f/Provider/needs_auth/HuggingFace.py +++ b/g4f/Provider/needs_auth/HuggingFace.py @@ -17,6 +17,7 @@ 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 @@ -149,14 +150,14 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin): 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 = "".join([ + 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}[INST] {question} [/INST]" + return f"{history}\n[INST] {question} [/INST]" def format_prompt_qwen(messages: Messages, do_continue: bool = False) -> str: prompt = "".join([ @@ -185,7 +186,7 @@ def format_prompt_custom(messages: Messages, end_token: str = "", do_continu 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 in ("mistral"): + 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"] diff --git a/g4f/Provider/needs_auth/HuggingFaceAPI.py b/g4f/Provider/needs_auth/HuggingFaceAPI.py index 521b2cc4..a3817b15 100644 --- a/g4f/Provider/needs_auth/HuggingFaceAPI.py +++ b/g4f/Provider/needs_auth/HuggingFaceAPI.py @@ -5,8 +5,8 @@ from .HuggingChat import HuggingChat class HuggingFaceAPI(OpenaiAPI): label = "HuggingFace (Inference API)" - url = "https://api-inference.huggingface.co" - login_url = "https://huggingface.co/settings/tokens" + parent = "HuggingFace" + url = "https://api-inference.huggingface.com" api_base = "https://api-inference.huggingface.co/v1" working = True default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct" diff --git a/g4f/gui/client/home.html b/g4f/gui/client/home.html index fa213061..5daa56b1 100644 --- a/g4f/gui/client/home.html +++ b/g4f/gui/client/home.html @@ -5,6 +5,10 @@ G4F GUI + + + +