From ab04d1d8943557e41191241686c10d5aa89bddd3 Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Sun, 26 Jan 2025 02:48:43 +0100 Subject: Increase max token in HuggingfaceAPI Restore browser instance on start up errors in nodriver Restored instances can be used as usual or to stop the browser Add demo modus to web ui for HuggingSpace Add rate limit support to web ui. Simply install flask_limiter Add home for demo with Access Token input and validation Add stripped model list for demo Add ignores for encoding error in web_search and file upload --- g4f/Provider/CablyAI.py | 7 +- g4f/Provider/DeepInfraChat.py | 1 - g4f/Provider/mini_max/HailuoAI.py | 4 +- g4f/Provider/mini_max/crypt.py | 17 +- g4f/Provider/needs_auth/HuggingChat.py | 5 +- g4f/Provider/needs_auth/HuggingFaceAPI.py | 10 +- g4f/api/__init__.py | 17 +- g4f/cli.py | 2 + g4f/gui/__init__.py | 3 +- g4f/gui/client/demo.html | 257 ++++++++++++++++++++++++++++++ g4f/gui/server/api.py | 5 +- g4f/gui/server/backend_api.py | 127 +++++++++------ g4f/gui/server/website.py | 7 +- g4f/models.py | 64 +++++--- g4f/requests/__init__.py | 20 ++- g4f/tools/files.py | 4 +- g4f/tools/web_search.py | 12 +- 17 files changed, 444 insertions(+), 118 deletions(-) create mode 100644 g4f/gui/client/demo.html diff --git a/g4f/Provider/CablyAI.py b/g4f/Provider/CablyAI.py index b82ee5d2..07e9fe7f 100644 --- a/g4f/Provider/CablyAI.py +++ b/g4f/Provider/CablyAI.py @@ -1,16 +1,15 @@ from __future__ import annotations from ..typing import AsyncResult, Messages -from .needs_auth import OpenaiAPI +from .needs_auth.OpenaiTemplate import OpenaiTemplate -class CablyAI(OpenaiAPI): - label = "CablyAI" +class CablyAI(OpenaiTemplate): url = "https://cablyai.com" login_url = None needs_auth = False api_base = "https://cablyai.com/v1" working = True - + default_model = "Cably-80B" models = [default_model] model_aliases = {"cably-80b": default_model} diff --git a/g4f/Provider/DeepInfraChat.py b/g4f/Provider/DeepInfraChat.py index e42d9a18..7aa1fd09 100644 --- a/g4f/Provider/DeepInfraChat.py +++ b/g4f/Provider/DeepInfraChat.py @@ -4,7 +4,6 @@ from ..typing import AsyncResult, Messages from .needs_auth.OpenaiTemplate import OpenaiTemplate class DeepInfraChat(OpenaiTemplate): - label = "DeepInfraChat" url = "https://deepinfra.com/chat" api_base = "https://api.deepinfra.com/v1/openai" working = True diff --git a/g4f/Provider/mini_max/HailuoAI.py b/g4f/Provider/mini_max/HailuoAI.py index 810120f7..c9b3e49c 100644 --- a/g4f/Provider/mini_max/HailuoAI.py +++ b/g4f/Provider/mini_max/HailuoAI.py @@ -7,7 +7,7 @@ from aiohttp import ClientSession, FormData from ...typing import AsyncResult, Messages from ..base_provider import AsyncAuthedProvider, ProviderModelMixin, format_prompt -from ..mini_max.crypt import CallbackResults, get_browser_callback, generate_yy_header +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 ... import debug @@ -71,7 +71,7 @@ class HailuoAI(AsyncAuthedProvider, ProviderModelMixin): data.add_field(name, str(value)) headers = { "token": token, - "yy": generate_yy_header(auth_result.path_and_query, form_data, "POST", timestamp) + "yy": generate_yy_header(auth_result.path_and_query, get_body_to_yy(form_data), timestamp) } async with session.post(f"{cls.url}{path_and_query}", data=data, headers=headers) as response: await raise_for_status(response) diff --git a/g4f/Provider/mini_max/crypt.py b/g4f/Provider/mini_max/crypt.py index c96491f4..197bda6e 100644 --- a/g4f/Provider/mini_max/crypt.py +++ b/g4f/Provider/mini_max/crypt.py @@ -22,27 +22,17 @@ def hash_function(base_string: str) -> str: """ return hashlib.md5(base_string.encode()).hexdigest() -def generate_yy_header(has_search_params_path: str, body: dict, method: str, time: int) -> str: +def generate_yy_header(has_search_params_path: str, body_to_yy: dict, time: int) -> str: """ Python equivalent of the generateYYHeader function. """ - body_to_yy=get_body_to_yy(body) - - if method and method.lower() == 'post': - s = body or {} - else: - s = {} - - s = json.dumps(s, ensure_ascii=True, sort_keys=True) - if body_to_yy: - s = body_to_yy # print("Encoded Path:", quote(has_search_params_path, "")) # print("Stringified Body:", s) # print("Hashed Time:", hash_function(str(time))) encoded_path = quote(has_search_params_path, "") time_hash = hash_function(str(time)) - combined_string = f"{encoded_path}_{s}{time_hash}ooui" + combined_string = f"{encoded_path}_{body_to_yy}{time_hash}ooui" # print("Combined String:", combined_string) # print("Hashed Combined String:", hash_function(combined_string)) @@ -56,6 +46,9 @@ def get_body_to_yy(l): # print("bodyToYY:", M) return M +def get_body_json(s): + return json.dumps(s, ensure_ascii=True, sort_keys=True) + async def get_browser_callback(auth_result: CallbackResults): async def callback(page: Tab): while not auth_result.token: diff --git a/g4f/Provider/needs_auth/HuggingChat.py b/g4f/Provider/needs_auth/HuggingChat.py index 36d292b7..bf5abb8f 100644 --- a/g4f/Provider/needs_auth/HuggingChat.py +++ b/g4f/Provider/needs_auth/HuggingChat.py @@ -65,10 +65,13 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin): "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", + ### API ### + "qwen-2-vl-7b": "Qwen/Qwen2-VL-7B-Instruct", + "gemma-2-27b": "google/gemma-2-27b-it", + "qvq-72b": "Qwen/QVQ-72B-Preview" } @classmethod diff --git a/g4f/Provider/needs_auth/HuggingFaceAPI.py b/g4f/Provider/needs_auth/HuggingFaceAPI.py index b36f0e0a..000dac90 100644 --- a/g4f/Provider/needs_auth/HuggingFaceAPI.py +++ b/g4f/Provider/needs_auth/HuggingFaceAPI.py @@ -14,6 +14,8 @@ class HuggingFaceAPI(OpenaiTemplate): 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): @@ -28,9 +30,13 @@ class HuggingFaceAPI(OpenaiTemplate): model: str, messages: Messages, api_base: str = None, + max_tokens: int = 2048, **kwargs ): if api_base is None: - api_base = f"https://api-inference.huggingface.co/models/{model}/v1" - async for chunk in super().create_async_generator(model, messages, api_base=api_base, **kwargs): + 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" + async for chunk in super().create_async_generator(model, messages, api_base=api_base, max_tokens=max_tokens, **kwargs): yield chunk \ No newline at end of file diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py index c0c26104..896367e4 100644 --- a/g4f/api/__init__.py +++ b/g4f/api/__init__.py @@ -73,7 +73,7 @@ def create_app(): api.register_validation_exception_handler() if AppConfig.gui: - gui_app = WSGIMiddleware(get_gui_app()) + gui_app = WSGIMiddleware(get_gui_app(AppConfig.demo)) app.mount("/", gui_app) # Read cookie files if not ignored @@ -96,6 +96,12 @@ def create_app_with_gui_and_debug(): AppConfig.gui = True return create_app() +def create_app_with_demo_and_debug(): + g4f.debug.logging = True + AppConfig.gui = True + AppConfig.demo = True + return create_app() + class ErrorResponse(Response): media_type = "application/json" @@ -121,6 +127,7 @@ class AppConfig: image_provider: str = None proxy: str = None gui: bool = False + demo: bool = False @classmethod def set_config(cls, **data): @@ -156,7 +163,7 @@ class Api: print(f"Register authentication key: {''.join(['*' for _ in range(len(AppConfig.g4f_api_key))])}") @self.app.middleware("http") async def authorization(request: Request, call_next): - if AppConfig.g4f_api_key is not None: + if AppConfig.g4f_api_key is not None or AppConfig.demo: try: user_g4f_api_key = await self.get_g4f_api_key(request) except HTTPException: @@ -167,7 +174,7 @@ class Api: return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED) if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key): return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN) - else: + elif not AppConfig.demo: if user_g4f_api_key is not None and path.startswith("/images/"): if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key): return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN) @@ -562,7 +569,9 @@ def run_api( host, port = bind.split(":") if port is None: port = DEFAULT_PORT - if AppConfig.gui and debug: + if AppConfig.demo and debug: + method = "create_app_with_demo_and_debug" + elif AppConfig.gui and debug: method = "create_app_with_gui_and_debug" else: method = "create_app_debug" if debug else "create_app" diff --git a/g4f/cli.py b/g4f/cli.py index 51721548..af872489 100644 --- a/g4f/cli.py +++ b/g4f/cli.py @@ -28,6 +28,7 @@ def get_api_parser(): api_parser.add_argument("--cookie-browsers", nargs="+", choices=[browser.__name__ for browser in g4f.cookies.browsers], default=[], help="List of browsers to access or retrieve cookies from. (incompatible with --reload and --workers)") api_parser.add_argument("--reload", action="store_true", help="Enable reloading.") + api_parser.add_argument("--demo", action="store_true", help="Enable demo modus.") return api_parser def main(): @@ -57,6 +58,7 @@ def run_api_args(args): proxy=args.proxy, model=args.model, gui=args.gui, + demo=args.demo, ) if args.cookie_browsers: g4f.cookies.browsers = [g4f.cookies[browser] for browser in args.cookie_browsers] diff --git a/g4f/gui/__init__.py b/g4f/gui/__init__.py index 4dc286ad..8dab783a 100644 --- a/g4f/gui/__init__.py +++ b/g4f/gui/__init__.py @@ -8,10 +8,11 @@ try: except ImportError as e: import_error = e -def get_gui_app(): +def get_gui_app(demo: bool = False): if import_error is not None: raise MissingRequirementsError(f'Install "gui" requirements | pip install -U g4f[gui]\n{import_error}') app = create_app() + app.demo = demo site = Website(app) for route in site.routes: diff --git a/g4f/gui/client/demo.html b/g4f/gui/client/demo.html new file mode 100644 index 00000000..dd0c078f --- /dev/null +++ b/g4f/gui/client/demo.html @@ -0,0 +1,257 @@ + + + +
+ + +