From e53483d85b5c8efd701e39bf56dd902b8744c755 Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Fri, 21 Feb 2025 04:36:54 +0100 Subject: Improve tools support in OpenaiTemplate and GeminiPro Update models in DDG, PerplexityLabs, Gemini Fix issues with curl_cffi in new versions --- g4f/Provider/DDG.py | 2 +- g4f/Provider/PerplexityLabs.py | 37 +++++++++++++++-------------- g4f/Provider/PollinationsAI.py | 9 +++---- g4f/Provider/hf/HuggingChat.py | 5 ++-- g4f/Provider/hf/HuggingFaceInference.py | 39 +++++++++++++++++-------------- g4f/Provider/hf/__init__.py | 2 +- g4f/Provider/hf_space/Janus_Pro_7B.py | 4 ++-- g4f/Provider/needs_auth/CopilotAccount.py | 4 +--- g4f/Provider/needs_auth/Gemini.py | 4 ++-- g4f/Provider/needs_auth/GeminiPro.py | 18 +++++++++++--- g4f/Provider/needs_auth/OpenaiChat.py | 24 +++++++------------ g4f/Provider/template/OpenaiTemplate.py | 7 +++--- 12 files changed, 83 insertions(+), 72 deletions(-) (limited to 'g4f/Provider') diff --git a/g4f/Provider/DDG.py b/g4f/Provider/DDG.py index 452f4e22..0b8c6680 100644 --- a/g4f/Provider/DDG.py +++ b/g4f/Provider/DDG.py @@ -42,7 +42,7 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin): "gpt-4": "gpt-4o-mini", "claude-3-haiku": "claude-3-haiku-20240307", "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct-Turbo", - "mixtral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1", + "mixtral-8x7b": "mistralai/Mistral-Small-24B-Instruct-2501", } last_request_time = 0 diff --git a/g4f/Provider/PerplexityLabs.py b/g4f/Provider/PerplexityLabs.py index c0ee5258..48383c02 100644 --- a/g4f/Provider/PerplexityLabs.py +++ b/g4f/Provider/PerplexityLabs.py @@ -5,7 +5,8 @@ import json from ..typing import AsyncResult, Messages from ..requests import StreamSession, raise_for_status -from ..providers.response import FinishReason +from ..errors import ResponseError +from ..providers.response import FinishReason, Sources from .base_provider import AsyncGeneratorProvider, ProviderModelMixin API_URL = "https://www.perplexity.ai/socket.io/" @@ -15,10 +16,11 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin): url = "https://labs.perplexity.ai" working = True - default_model = "sonar-pro" + default_model = "r1-1776" models = [ - "sonar", default_model, + "sonar-pro", + "sonar", "sonar-reasoning", "sonar-reasoning-pro", ] @@ -32,19 +34,10 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin): **kwargs ) -> AsyncResult: headers = { - "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0", - "Accept": "*/*", - "Accept-Language": "de,en-US;q=0.7,en;q=0.3", - "Accept-Encoding": "gzip, deflate, br", "Origin": cls.url, - "Connection": "keep-alive", "Referer": f"{cls.url}/", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "same-site", - "TE": "trailers", } - async with StreamSession(headers=headers, proxies={"all": proxy}) as session: + async with StreamSession(headers=headers, proxy=proxy, impersonate="chrome") as session: t = format(random.getrandbits(32), "08x") async with session.get( f"{API_URL}?EIO=4&transport=polling&t={t}" @@ -60,17 +53,22 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin): ) as response: await raise_for_status(response) assert await response.text() == "OK" + async with session.get( + f"{API_URL}?EIO=4&transport=polling&t={t}&sid={sid}", + data=post_data + ) as response: + await raise_for_status(response) + assert (await response.text()).startswith("40") async with session.ws_connect(f"{WS_URL}?EIO=4&transport=websocket&sid={sid}", autoping=False) as ws: await ws.send_str("2probe") assert(await ws.receive_str() == "3probe") await ws.send_str("5") - assert(await ws.receive_str()) assert(await ws.receive_str() == "6") message_data = { - "version": "2.16", + "version": "2.18", "source": "default", "model": model, - "messages": messages + "messages": messages, } await ws.send_str("42" + json.dumps(["perplexity_labs", message_data])) last_message = 0 @@ -82,12 +80,15 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin): await ws.send_str("3") continue try: + if last_message == 0 and model == cls.default_model: + yield "" data = json.loads(message[2:])[1] yield data["output"][last_message:] last_message = len(data["output"]) if data["final"]: + if data["citations"]: + yield Sources(data["citations"]) yield FinishReason("stop") break except Exception as e: - print(f"Error processing message: {message} - {e}") - raise RuntimeError(f"Message: {message}") from e + raise ResponseError(f"Message: {message}") from e diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py index 1aee29cf..ebdff660 100644 --- a/g4f/Provider/PollinationsAI.py +++ b/g4f/Provider/PollinationsAI.py @@ -122,9 +122,9 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): except ModelNotFoundError: if model not in cls.image_models: raise - + if not cache and seed is None: - seed = random.randint(0, 10000) + seed = random.randint(1000, 999999) if model in cls.image_models: async for chunk in cls._generate_image( @@ -182,9 +182,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin): } params = {k: v for k, v in params.items() if v is not None} query = "&".join(f"{k}={quote_plus(v)}" for k, v in params.items()) - url = f"{cls.image_api_endpoint}prompt/{quote_plus(prompt)}?{query}" + prefix = f"{model}_{seed}" if seed is not None else model + url = f"{cls.image_api_endpoint}prompt/{prefix}_{quote_plus(prompt)}?{query}" yield ImagePreview(url, prompt) - + async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session: async with session.get(url, allow_redirects=True) as response: await raise_for_status(response) diff --git a/g4f/Provider/hf/HuggingChat.py b/g4f/Provider/hf/HuggingChat.py index 29f5b9c6..83ef34eb 100644 --- a/g4f/Provider/hf/HuggingChat.py +++ b/g4f/Provider/hf/HuggingChat.py @@ -39,14 +39,15 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin): default_model = default_model model_aliases = model_aliases image_models = image_models + text_models = fallback_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 = re.sub(r',parameters:{[^}]+?}', '', text) text = text.replace('void 0', 'null') def add_quotation_mark(match): return f'{match.group(1)}"{match.group(2)}":' @@ -56,7 +57,7 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin): 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}") + debug.error(f"{cls.__name__}: Error reading models: {type(e).__name__}: {e}") cls.models = [*fallback_models] return cls.models diff --git a/g4f/Provider/hf/HuggingFaceInference.py b/g4f/Provider/hf/HuggingFaceInference.py index 61d0c86b..1ada7347 100644 --- a/g4f/Provider/hf/HuggingFaceInference.py +++ b/g4f/Provider/hf/HuggingFaceInference.py @@ -10,8 +10,8 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_p from ...errors import ModelNotFoundError, ModelNotSupportedError, ResponseError from ...requests import StreamSession, raise_for_status from ...providers.response import FinishReason, ImageResponse -from ..helper import format_image_prompt -from .models import default_model, default_image_model, model_aliases, fallback_models +from ..helper import format_image_prompt, get_last_user_message +from .models import default_model, default_image_model, model_aliases, fallback_models, image_models from ... import debug class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin): @@ -22,26 +22,26 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin): default_model = default_model default_image_model = default_image_model model_aliases = model_aliases + image_models = image_models @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" - response = requests.get(url) - response.raise_for_status() + if not cls.models: + models = fallback_models.copy() + url = "https://huggingface.co/api/models?inference=warm&pipeline_tag=text-generation" + response = requests.get(url) + if response.ok: extra_models = [model["id"] for model in response.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" - response = requests.get(url) - response.raise_for_status() - cls.image_models = [model["id"] for model in response.json() if model.get("trendingScore", 0) >= 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 + url = "https://huggingface.co/api/models?pipeline_tag=text-to-image" + response = requests.get(url) + if response.ok: + cls.image_models = [model["id"] for model in response.json() if model.get("trendingScore", 0) >= 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( @@ -57,6 +57,7 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin): prompt: str = None, action: str = None, extra_data: dict = {}, + seed: int = None, **kwargs ) -> AsyncResult: try: @@ -104,7 +105,7 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin): if pipeline_tag == "text-to-image": stream = False inputs = format_image_prompt(messages, prompt) - payload = {"inputs": inputs, "parameters": {"seed": random.randint(0, 2**32), **extra_data}} + payload = {"inputs": inputs, "parameters": {"seed": random.randint(0, 2**32) if seed is None else seed, **extra_data}} elif pipeline_tag in ("text-generation", "image-text-to-text"): model_type = None if "config" in model_data and "model_type" in model_data["config"]: @@ -116,11 +117,13 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin): if len(messages) > 6: messages = messages[:3] + messages[-3:] else: - messages = [m for m in messages if m["role"] == "system"] + [messages[-1]] + messages = [m for m in messages if m["role"] == "system"] + [get_last_user_message(messages)] 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 + if seed is not None: + params["seed"] = seed payload = {"inputs": inputs, "parameters": params, "stream": stream} else: raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} pipeline_tag: {pipeline_tag}") diff --git a/g4f/Provider/hf/__init__.py b/g4f/Provider/hf/__init__.py index a83ae3fb..ec103831 100644 --- a/g4f/Provider/hf/__init__.py +++ b/g4f/Provider/hf/__init__.py @@ -48,7 +48,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin): except Exception as e: if is_started: raise e - debug.log(f"Inference failed: {e.__class__.__name__}: {e}") + debug.error(f"{cls.__name__} {type(e).__name__}; {e}") if not cls.image_models: cls.get_models() if model in cls.image_models: diff --git a/g4f/Provider/hf_space/Janus_Pro_7B.py b/g4f/Provider/hf_space/Janus_Pro_7B.py index 49b4f5bb..2021cd20 100644 --- a/g4f/Provider/hf_space/Janus_Pro_7B.py +++ b/g4f/Provider/hf_space/Janus_Pro_7B.py @@ -3,7 +3,7 @@ from __future__ import annotations import json import uuid import re -import time +import random from datetime import datetime, timezone, timedelta import urllib.parse @@ -88,7 +88,7 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin): prompt = format_prompt(messages) if prompt is None and conversation is None else prompt prompt = format_image_prompt(messages, prompt) if seed is None: - seed = int(time.time()) + seed = random.randint(1000, 999999) session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash") async with StreamSession(proxy=proxy, impersonate="chrome") as session: diff --git a/g4f/Provider/needs_auth/CopilotAccount.py b/g4f/Provider/needs_auth/CopilotAccount.py index 9946ef25..2ac667cc 100644 --- a/g4f/Provider/needs_auth/CopilotAccount.py +++ b/g4f/Provider/needs_auth/CopilotAccount.py @@ -32,9 +32,7 @@ class CopilotAccount(AsyncAuthedProvider, Copilot): except NoValidHarFileError as h: debug.log(f"Copilot: {h}") if has_nodriver: - login_url = os.environ.get("G4F_LOGIN_URL") - if login_url: - yield RequestLogin(cls.label, login_url) + yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", "")) Copilot._access_token, Copilot._cookies = await get_access_token_and_cookies(cls.url, proxy) else: raise h diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py index 2d120ac4..1e954183 100644 --- a/g4f/Provider/needs_auth/Gemini.py +++ b/g4f/Provider/needs_auth/Gemini.py @@ -65,7 +65,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin): default_image_model = default_model default_vision_model = default_model image_models = [default_image_model] - models = [default_model, "gemini-1.5-flash", "gemini-1.5-pro"] + models = [default_model, "gemini-2.0"] synthesize_content_type = "audio/vnd.wav" @@ -179,7 +179,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin): yield Conversation(response_part[1][0], response_part[1][1], response_part[4][0][0]) content = response_part[4][0][1][0] except (ValueError, KeyError, TypeError, IndexError) as e: - debug.log(f"{cls.__name__}:{e.__class__.__name__}:{e}") + debug.error(f"{cls.__name__} {type(e).__name__}: {e}") continue match = re.search(r'\[Imagen of (.*?)\]', content) if match: diff --git a/g4f/Provider/needs_auth/GeminiPro.py b/g4f/Provider/needs_auth/GeminiPro.py index 9d6de82c..a626ce8a 100644 --- a/g4f/Provider/needs_auth/GeminiPro.py +++ b/g4f/Provider/needs_auth/GeminiPro.py @@ -51,7 +51,9 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin): ] cls.models.sort() except Exception as e: - debug.log(e) + debug.error(e) + if api_key is not None: + raise MissingAuthError("Invalid API key") return cls.fallback_models return cls.models @@ -111,8 +113,18 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin): "topK": kwargs.get("top_k"), }, "tools": [{ - "functionDeclarations": tools - }] if tools else None + "function_declarations": [{ + "name": tool["function"]["name"], + "description": tool["function"]["description"], + "parameters": { + "type": "object", + "properties": {key: { + "type": value["type"], + "description": value["title"] + } for key, value in tool["function"]["parameters"]["properties"].items()} + }, + } for tool in tools] + }] if tools else None } system_prompt = "\n".join( message["content"] diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py index cbccd1a2..9f07d9b5 100644 --- a/g4f/Provider/needs_auth/OpenaiChat.py +++ b/g4f/Provider/needs_auth/OpenaiChat.py @@ -322,8 +322,8 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin): try: image_requests = await cls.upload_images(session, auth_result, images) if images else None except Exception as e: - debug.log("OpenaiChat: Upload image failed") - debug.log(f"{e.__class__.__name__}: {e}") + debug.error("OpenaiChat: Upload image failed") + debug.error(e) model = cls.get_model(model) if conversation is None: conversation = Conversation(conversation_id, str(uuid.uuid4()), getattr(auth_result, "cookies", {}).get("oai-did")) @@ -360,12 +360,14 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin): # if auth_result.arkose_token is None: # raise MissingAuthError("No arkose token found in .har file") if "proofofwork" in chat_requirements: - if getattr(auth_result, "proof_token", None) is None: - auth_result.proof_token = get_config(auth_result.headers.get("user-agent")) + user_agent = getattr(auth_result, "headers", {}).get("user-agent") + proof_token = getattr(auth_result, "proof_token", None) + if proof_token is None: + auth_result.proof_token = get_config(user_agent) proofofwork = generate_proof_token( **chat_requirements["proofofwork"], - user_agent=getattr(auth_result, "headers", {}).get("user-agent"), - proof_token=getattr(auth_result, "proof_token", None) + user_agent=user_agent, + proof_token=proof_token ) [debug.log(text) for text in ( #f"Arkose: {'False' if not need_arkose else auth_result.arkose_token[:12]+'...'}", @@ -425,8 +427,8 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin): ) as response: cls._update_request_args(auth_result, session) if response.status == 403: - auth_result.proof_token = None cls.request_config.proof_token = None + raise MissingAuthError("Access token is not valid") await raise_for_status(response) buffer = u"" async for line in response.iter_lines(): @@ -469,14 +471,6 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin): await asyncio.sleep(5) else: break - yield Parameters(**{ - "action": "continue" if conversation.finish_reason == "max_tokens" else "variant", - "conversation": conversation.get_dict(), - "proof_token": cls.request_config.proof_token, - "cookies": cls._cookies, - "headers": cls._headers, - "web_search": web_search, - }) yield FinishReason(conversation.finish_reason) @classmethod diff --git a/g4f/Provider/template/OpenaiTemplate.py b/g4f/Provider/template/OpenaiTemplate.py index 54938286..d8b54578 100644 --- a/g4f/Provider/template/OpenaiTemplate.py +++ b/g4f/Provider/template/OpenaiTemplate.py @@ -42,7 +42,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin if cls.sort_models: cls.models.sort() except Exception as e: - debug.log(e) + debug.error(e) return cls.fallback_models return cls.models @@ -65,7 +65,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin prompt: str = None, headers: dict = None, impersonate: str = None, - tools: Optional[list] = None, + extra_parameters: list[str] = ["tools", "parallel_tool_calls", "", "reasoning_effort", "logit_bias"], extra_data: dict = {}, **kwargs ) -> AsyncResult: @@ -112,6 +112,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin } ] messages[-1] = last_message + extra_parameters = {key: kwargs[key] for key in extra_parameters if key in kwargs} data = filter_none( messages=messages, model=model, @@ -120,7 +121,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin top_p=top_p, stop=stop, stream=stream, - tools=tools, + **extra_parameters, **extra_data ) if api_endpoint is None: -- cgit v1.2.3