From 1d00be5b918abf5608efcece912fa00e0c2a78bd Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:25:51 +0100 Subject: Fix api_key in HuggingFace provider Split PollinationsAI provider in two provider Update model list in CablyAI Return backup url, if copy images failed Update url for logging in UI --- g4f/Provider/CablyAI.py | 8 ++--- g4f/Provider/PollinationsAI.py | 24 ++++++++------- g4f/Provider/PollinationsImage.py | 58 +++++++++++++++++++++++++++++++++++++ g4f/Provider/__init__.py | 1 + g4f/Provider/hf/HuggingFaceAPI.py | 4 ++- g4f/gui/client/static/js/chat.v1.js | 8 ++--- g4f/image/copy_images.py | 11 ++++++- g4f/tools/run_tools.py | 2 +- 8 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 g4f/Provider/PollinationsImage.py diff --git a/g4f/Provider/CablyAI.py b/g4f/Provider/CablyAI.py index d13d7a58..d3f8cbb3 100644 --- a/g4f/Provider/CablyAI.py +++ b/g4f/Provider/CablyAI.py @@ -5,14 +5,14 @@ from .template import OpenaiTemplate class CablyAI(OpenaiTemplate): url = "https://cablyai.com" - login_url = None + login_url = url needs_auth = False api_base = "https://cablyai.com/v1" working = True - default_model = "Cably-80B" - models = [default_model] - model_aliases = {"cably-80b": default_model} + default_model = "o3-mini-low" + fallback_models = [default_model, "Cably-80B"] + model_aliases = {"cably-80b": "Cably-80B"} @classmethod def create_async_generator( diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py index 3e784eb7..caf1f7f0 100644 --- a/g4f/Provider/PollinationsAI.py +++ b/g4f/Provider/PollinationsAI.py @@ -11,6 +11,7 @@ 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 +from ..errors import ModelNotFoundError from ..requests.raise_for_status import raise_for_status from ..requests.aiohttp import get_connector from ..providers.response import ImageResponse, ImagePreview, FinishReason, Usage, Reasoning @@ -64,20 +65,20 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): "deepseek-r1": "deepseek-reasoner", ### Image Models ### - "sdxl-turbo": "turbo", + "sdxl-turbo": "turbo", + "flux-schnell": "flux", } text_models = [] @classmethod def get_models(cls, **kwargs): - if not cls.image_models: + if not cls.text_models: url = "https://image.pollinations.ai/models" response = requests.get(url) raise_for_status(response) - cls.image_models = response.json() - cls.image_models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models])) + new_image_models = response.json() + cls.extra_image_models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models, *new_image_models])) - if not cls.text_models: url = "https://text.pollinations.ai/models" response = requests.get(url) raise_for_status(response) @@ -87,8 +88,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): if model not in cls.extra_text_models ] cls.text_models = list(dict.fromkeys(combined_text)) - - return list(dict.fromkeys([*cls.text_models, *cls.image_models])) + return cls.text_models @classmethod async def create_async_generator( @@ -115,11 +115,15 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): ) -> AsyncResult: if images is not None and not model: model = cls.default_vision_model - model = cls.get_model(model) + try: + model = cls.get_model(model) + except ModelNotFoundError: + if model not in cls.extra_image_models: + raise if not cache and seed is None: - seed = random.randint(0, 100000) + seed = random.randint(0, 10000) - if model in cls.image_models: + if model in cls.image_models and model not in cls.extra_image_models: async for chunk in cls._generate_image( model=model, prompt=format_image_prompt(messages, prompt), diff --git a/g4f/Provider/PollinationsImage.py b/g4f/Provider/PollinationsImage.py new file mode 100644 index 00000000..35ab669e --- /dev/null +++ b/g4f/Provider/PollinationsImage.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import Optional + +from .helper import format_image_prompt +from ..typing import AsyncResult, Messages +from .PollinationsAI import PollinationsAI + +class PollinationsImage(PollinationsAI): + # From: https://pollinations.ai/static/js/components/FeedImage/ImageEditor.js + image_models = [ + "flux", + "flux-pro", + "flux-realism", + "flux-anime", + "flux-3d", + "flux-cablyai", + "turbo", + ] + default_model = "flux" + default_vision_model = None + default_image_model = default_model + + @classmethod + def get_models(cls, **kwargs): + if not cls.models: + cls.models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models, *PollinationsAI.extra_image_models])) + return cls.models + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + proxy: str = None, + prompt: str = None, + width: int = 1024, + height: int = 1024, + seed: Optional[int] = None, + nologo: bool = True, + private: bool = False, + enhance: bool = False, + safe: bool = False, + **kwargs + ) -> AsyncResult: + async for chunk in cls._generate_image( + model=model, + prompt=format_image_prompt(messages, prompt), + proxy=proxy, + width=width, + height=height, + seed=seed, + nologo=nologo, + private=private, + enhance=enhance, + safe=safe + ): + yield chunk \ No newline at end of file diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index c7ad606f..e0f37138 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -39,6 +39,7 @@ from .PerplexityLabs import PerplexityLabs from .Pi import Pi from .Pizzagpt import Pizzagpt from .PollinationsAI import PollinationsAI +from .PollinationsImage import PollinationsImage from .Prodia import Prodia from .TeachAnything import TeachAnything from .You import You diff --git a/g4f/Provider/hf/HuggingFaceAPI.py b/g4f/Provider/hf/HuggingFaceAPI.py index 08e03e44..0d392385 100644 --- a/g4f/Provider/hf/HuggingFaceAPI.py +++ b/g4f/Provider/hf/HuggingFaceAPI.py @@ -38,6 +38,7 @@ class HuggingFaceAPI(OpenaiTemplate): model: str, messages: Messages, api_base: str = None, + api_key: str = None, max_tokens: int = 2048, max_inputs_lenght: int = 10000, images: ImagesType = None, @@ -50,6 +51,7 @@ class HuggingFaceAPI(OpenaiTemplate): api_base = f"https://api-inference.huggingface.co/models/{model_name}/v1" async with StreamSession( timeout=30, + headers=cls.get_headers(False, api_key), ) as session: async with session.get(f"https://huggingface.co/api/models/{model}") as response: if response.status == 404: @@ -71,7 +73,7 @@ class HuggingFaceAPI(OpenaiTemplate): 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, images=images, **kwargs): + async for chunk in super().create_async_generator(model, messages, api_base=api_base, api_key=api_key, max_tokens=max_tokens, images=images, **kwargs): yield chunk def calculate_lenght(messages: Messages) -> int: diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js index 81706005..8233d13d 100644 --- a/g4f/gui/client/static/js/chat.v1.js +++ b/g4f/gui/client/static/js/chat.v1.js @@ -1959,7 +1959,7 @@ async function on_api() { messageInput.addEventListener("keydown", async (evt) => { if (prompt_lock) return; // If not mobile and not shift enter - let do_enter = messageInput.value.endsWith("\n\n"); + let do_enter = messageInput.value.endsWith("\n\n\n\n"); if (do_enter || !window.matchMedia("(pointer:coarse)").matches && evt.keyCode === 13 && !evt.shiftKey) { evt.preventDefault(); console.log("pressed enter"); @@ -2447,11 +2447,11 @@ async function api(ressource, args=null, files=null, message_id=null, scroll=tru return; } } else if (args) { - if (ressource == "log") { - if (!document.getElementById("report_error").checked) { + if (ressource in ("log", "usage")) { + if (ressource == "log" && !document.getElementById("report_error").checked) { return; } - url = `https://roxky-g4f-demo.hf.space${url}`; + url = `https://roxky-g4f-backup.hf.space${url}`; } headers['content-type'] = 'application/json'; response = await fetch(url, { diff --git a/g4f/image/copy_images.py b/g4f/image/copy_images.py index 051d10c7..6afa446c 100644 --- a/g4f/image/copy_images.py +++ b/g4f/image/copy_images.py @@ -28,6 +28,15 @@ def get_image_extension(image: str) -> str: def ensure_images_dir(): os.makedirs(images_dir, exist_ok=True) +def get_source_url(image: str, default: str = None) -> str: + source_url = image.split("url=", 1) + if len(source_url) > 1: + source_url = source_url[1] + source_url = source_url.replace("%2F", "/").replace("%3A", ":").replace("%3F", "?").replace("%3D", "=") + if source_url.startswith("https://"): + return source_url + return default + async def copy_images( images: list[str], cookies: Optional[Cookies] = None, @@ -68,7 +77,7 @@ async def copy_images( f.write(chunk) except ClientError as e: debug.log(f"copy_images failed: {e.__class__.__name__}: {e}") - return image + return get_source_url(image, image) if "." not in target: with open(target, "rb") as f: extension = is_accepted_format(f.read(12)).split("/")[-1] diff --git a/g4f/tools/run_tools.py b/g4f/tools/run_tools.py index 786342d2..528abf4f 100644 --- a/g4f/tools/run_tools.py +++ b/g4f/tools/run_tools.py @@ -154,7 +154,7 @@ def iter_run_tools( if not isinstance(chunk, str): yield chunk continue - if "" in chunk: + if "" in chunk and not "``" in chunk: if chunk != "": chunk = chunk.split("", 1) if len(chunk) > 0 and chunk[0]: -- cgit v1.2.3 From c4e4d340c4f04005ae10398f08d5d0436d7882e8 Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:46:30 +0100 Subject: Update PollinationsImage provider --- g4f/Provider/PollinationsAI.py | 17 +++++++++++++---- g4f/Provider/PollinationsImage.py | 16 +++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py index caf1f7f0..1a86ba76 100644 --- a/g4f/Provider/PollinationsAI.py +++ b/g4f/Provider/PollinationsAI.py @@ -37,9 +37,18 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): # Models configuration default_model = "openai" - default_image_model = "flux" default_vision_model = "gpt-4o" - extra_image_models = ["midjourney", "dall-e-3", "flux-pro"] + extra_image_models = [ + "flux", + "flux-pro", + "flux-realism", + "flux-anime", + "flux-3d", + "flux-cablyai", + "turbo", + "midjourney", + "dall-e-3", + ] vision_models = [default_vision_model, "gpt-4o-mini"] reasoning_models = ['deepseek-reasoner', 'deepseek-r1'] extra_text_models = ["claude", "claude-email", "p1"] + vision_models + reasoning_models @@ -77,7 +86,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): response = requests.get(url) raise_for_status(response) new_image_models = response.json() - cls.extra_image_models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models, *new_image_models])) + cls.extra_image_models = list(dict.fromkeys([*cls.extra_image_models, *new_image_models])) url = "https://text.pollinations.ai/models" response = requests.get(url) @@ -123,7 +132,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): if not cache and seed is None: seed = random.randint(0, 10000) - if model in cls.image_models and model not in cls.extra_image_models: + if model in cls.image_models or model in cls.extra_image_models: async for chunk in cls._generate_image( model=model, prompt=format_image_prompt(messages, prompt), diff --git a/g4f/Provider/PollinationsImage.py b/g4f/Provider/PollinationsImage.py index 35ab669e..eb7600be 100644 --- a/g4f/Provider/PollinationsImage.py +++ b/g4f/Provider/PollinationsImage.py @@ -7,25 +7,15 @@ from ..typing import AsyncResult, Messages from .PollinationsAI import PollinationsAI class PollinationsImage(PollinationsAI): - # From: https://pollinations.ai/static/js/components/FeedImage/ImageEditor.js - image_models = [ - "flux", - "flux-pro", - "flux-realism", - "flux-anime", - "flux-3d", - "flux-cablyai", - "turbo", - ] default_model = "flux" default_vision_model = None default_image_model = default_model @classmethod def get_models(cls, **kwargs): - if not cls.models: - cls.models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models, *PollinationsAI.extra_image_models])) - return cls.models + if not cls.image_models: + cls.image_models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models])) + return cls.image_models @classmethod async def create_async_generator( -- cgit v1.2.3