From 688640b764c3dde1f3a85ec604adece760b6c62c Mon Sep 17 00:00:00 2001 From: Hexye <65314629+HexyeDEV@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:47:54 +0100 Subject: [PATCH 01/15] Add ItalyGPT provider --- g4f/Provider/ItalyGPT.py | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 g4f/Provider/ItalyGPT.py diff --git a/g4f/Provider/ItalyGPT.py b/g4f/Provider/ItalyGPT.py new file mode 100644 index 00000000..e13fc0ee --- /dev/null +++ b/g4f/Provider/ItalyGPT.py @@ -0,0 +1,45 @@ +from .base_provider import AsyncGeneratorProvider, ProviderModelMixin +from ..typing import AsyncResult, Messages +from aiohttp import ClientSession + +class ItalyGPT(AsyncGeneratorProvider, ProviderModelMixin): + label = "ItalyGPT" + url = "https://italygpt.it" + working = True + supports_system_message = True + supports_message_history = True + + default_model = "gpt-4o" + models = [default_model] + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + stream: bool = True, + proxy: str = None, + **kwargs + ) -> AsyncResult: + model = cls.get_model(model) + headers = { + "content-type": "application/json", + "origin": "https://italygpt.it", + "referer": "https://italygpt.it/", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" + } + payload = { + "messages": messages, + "stream": stream, + } + async with ClientSession() as session: + async with session.post( + f"{cls.url}/api/chat/", + json=payload, + headers=headers, + proxy=proxy, + ) as resp: + resp.raise_for_status() + async for chunk in resp.content: + if chunk: + yield chunk.decode() From 6be76e3e843bf9b225007d06a097a39da3859093 Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Sat, 29 Nov 2025 05:12:23 +0100 Subject: [PATCH 02/15] Add support for gemini-3-pro model in Gemini provider --- g4f/Provider/needs_auth/Gemini.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py index fd7a4585..44e38728 100644 --- a/g4f/Provider/needs_auth/Gemini.py +++ b/g4f/Provider/needs_auth/Gemini.py @@ -71,6 +71,7 @@ models = { "gemini-2.0-flash-thinking": {"x-goog-ext-525001261-jspb": '[null,null,null,null,"9c17b1863f581b8a"]'}, "gemini-2.0-flash-thinking-with-apps": {"x-goog-ext-525001261-jspb": '[null,null,null,null,"f8f8f5ea629f5d37"]'}, # Currently used models + "gemini-3-pro": {"x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]'}, "gemini-2.5-pro": {"x-goog-ext-525001261-jspb": '[1,null,null,null,"61530e79959ab139",null,null,null,[4]]'}, "gemini-2.5-flash": {"x-goog-ext-525001261-jspb": '[1,null,null,null,"9ec249fc9ad08861",null,null,null,[4]]'}, "gemini-audio": {} @@ -89,7 +90,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin): default_vision_model = default_model image_models = [default_image_model] models = [ - default_model, "gemini-2.5-flash", "gemini-2.5-pro" + default_model, "gemini-3-pro", "gemini-2.5-flash", "gemini-2.5-pro" ] synthesize_content_type = "audio/vnd.wav" From d76e56a66f70d7c7764975aa481927d046f62a5d Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 29 Nov 2025 06:15:05 +0200 Subject: [PATCH 03/15] fix error (#3265) fix error --- g4f/Provider/needs_auth/OpenaiChat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py index 740c7c50..6e8b51f5 100644 --- a/g4f/Provider/needs_auth/OpenaiChat.py +++ b/g4f/Provider/needs_auth/OpenaiChat.py @@ -162,7 +162,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin): hasher.update(data_bytes) image_hash = hasher.hexdigest() cache_file = ImagesCache.get(image_hash) - if cls.image_cache and file: + if cls.image_cache and cache_file: debug.log("Using cached image") return ImageRequest(cache_file) extension, mime_type = detect_file_type(data_bytes) From 7b32f89eca5b11032d72464717d346c96b34c262 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:17:42 +0000 Subject: [PATCH 04/15] Initial plan From f0ea4c5b95d67312e0c183e859ef7bfcecd0fdf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:22:02 +0000 Subject: [PATCH 05/15] Add GradientNetwork provider for chat.gradient.network Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/GradientNetwork.py | 131 ++++++++++++++++++++++++++++++++ g4f/Provider/__init__.py | 1 + 2 files changed, 132 insertions(+) create mode 100644 g4f/Provider/GradientNetwork.py diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py new file mode 100644 index 00000000..b9286d0b --- /dev/null +++ b/g4f/Provider/GradientNetwork.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import json + +from aiohttp import ClientSession + +from ..typing import AsyncResult, Messages +from ..providers.response import Reasoning +from .base_provider import AsyncGeneratorProvider, ProviderModelMixin + + +class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): + """ + Provider for chat.gradient.network + Supports streaming text generation with various Qwen models. + """ + label = "Gradient Network" + url = "https://chat.gradient.network" + api_endpoint = "https://chat.gradient.network/api/generate" + + working = True + needs_auth = False + supports_stream = True + supports_system_message = True + supports_message_history = True + + default_model = "qwen3-235b" + models = [ + default_model, + "qwen3-32b", + "deepseek-r1-0528", + "deepseek-v3-0324", + "llama-4-maverick", + ] + model_aliases = { + "qwen-3-235b": "qwen3-235b", + "deepseek-r1": "deepseek-r1-0528", + "deepseek-v3": "deepseek-v3-0324", + } + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + proxy: str = None, + temperature: float = None, + max_tokens: int = None, + enable_thinking: bool = False, + **kwargs + ) -> AsyncResult: + """ + Create an async generator for streaming chat responses. + + Args: + model: The model name to use + messages: List of message dictionaries + proxy: Optional proxy URL + temperature: Optional temperature parameter + max_tokens: Optional max tokens parameter + enable_thinking: Enable the thinking/analysis channel + **kwargs: Additional arguments + + Yields: + str: Content chunks from the response + Reasoning: Thinking content when enable_thinking is True + """ + model = cls.get_model(model) + + headers = { + "Accept": "application/x-ndjson", + "Content-Type": "application/json", + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", + "Origin": cls.url, + "Referer": f"{cls.url}/", + } + + payload = { + "model": model, + "messages": messages, + } + + if temperature is not None: + payload["temperature"] = temperature + if max_tokens is not None: + payload["max_tokens"] = max_tokens + if enable_thinking: + payload["enableThinking"] = True + + async with ClientSession(headers=headers) as session: + async with session.post( + cls.api_endpoint, + json=payload, + proxy=proxy + ) as response: + response.raise_for_status() + + async for line_bytes in response.content: + if not line_bytes: + continue + + line = line_bytes.decode("utf-8").strip() + if not line: + continue + + try: + data = json.loads(line) + msg_type = data.get("type") + + if msg_type == "text": + # Regular text content + content = data.get("data") + if content: + yield content + + elif msg_type == "thinking": + # Thinking/reasoning content + content = data.get("data") + if content: + yield Reasoning(content) + + elif msg_type == "done": + # Stream complete + break + + # Ignore clusterInfo and blockUpdate messages + # as they are for GPU cluster visualization only + + except json.JSONDecodeError: + # Skip non-JSON lines + continue diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index 826ac4c4..ea51e380 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -48,6 +48,7 @@ from .Copilot import Copilot from .DeepInfra import DeepInfra from .EasyChat import EasyChat from .GLM import GLM +from .GradientNetwork import GradientNetwork from .LambdaChat import LambdaChat from .Mintlify import Mintlify from .OIVSCodeSer import OIVSCodeSer2, OIVSCodeSer0501 From 07883bc9f07afc6c6633815f5e4c34ea64aeb170 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:23:46 +0000 Subject: [PATCH 06/15] Initial plan From da4d7d118dcffbf59a5a7c2a7f495e04bdfd9319 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:24:09 +0000 Subject: [PATCH 07/15] Use StreamSession for proper line-by-line NDJSON parsing Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/GradientNetwork.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py index b9286d0b..d5dc1919 100644 --- a/g4f/Provider/GradientNetwork.py +++ b/g4f/Provider/GradientNetwork.py @@ -2,10 +2,9 @@ from __future__ import annotations import json -from aiohttp import ClientSession - from ..typing import AsyncResult, Messages from ..providers.response import Reasoning +from ..requests import StreamSession from .base_provider import AsyncGeneratorProvider, ProviderModelMixin @@ -58,7 +57,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): proxy: Optional proxy URL temperature: Optional temperature parameter max_tokens: Optional max tokens parameter - enable_thinking: Enable the thinking/analysis channel + enable_thinking: Enable the thinking/analysis channel (maps to enableThinking in API) **kwargs: Additional arguments Yields: @@ -87,19 +86,14 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): if enable_thinking: payload["enableThinking"] = True - async with ClientSession(headers=headers) as session: + async with StreamSession(headers=headers, proxy=proxy) as session: async with session.post( cls.api_endpoint, json=payload, - proxy=proxy ) as response: response.raise_for_status() - async for line_bytes in response.content: - if not line_bytes: - continue - - line = line_bytes.decode("utf-8").strip() + async for line in response.iter_lines(): if not line: continue @@ -127,5 +121,5 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): # as they are for GPU cluster visualization only except json.JSONDecodeError: - # Skip non-JSON lines + # Skip non-JSON lines (may be partial data or empty) continue From f57663cbe81b9e93399aeb29bdfa39929f532146 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:25:22 +0000 Subject: [PATCH 08/15] Address code review: pass enable_thinking value directly, explicit skip for cluster messages Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/GradientNetwork.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py index d5dc1919..b6aec90c 100644 --- a/g4f/Provider/GradientNetwork.py +++ b/g4f/Provider/GradientNetwork.py @@ -84,7 +84,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): if max_tokens is not None: payload["max_tokens"] = max_tokens if enable_thinking: - payload["enableThinking"] = True + payload["enableThinking"] = enable_thinking async with StreamSession(headers=headers, proxy=proxy) as session: async with session.post( @@ -117,8 +117,9 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): # Stream complete break - # Ignore clusterInfo and blockUpdate messages - # as they are for GPU cluster visualization only + elif msg_type in ("clusterInfo", "blockUpdate"): + # Skip GPU cluster visualization messages + continue except json.JSONDecodeError: # Skip non-JSON lines (may be partial data or empty) From c364425250e3070eaf0fc63c45bd9c18c5c1ab4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:32:32 +0000 Subject: [PATCH 09/15] Add BAAI_Ling provider for Ling-1T model Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/hf_space/BAAI_Ling.py | 104 +++++++++++++++++++++++++++++ g4f/Provider/hf_space/__init__.py | 2 + 2 files changed, 106 insertions(+) create mode 100644 g4f/Provider/hf_space/BAAI_Ling.py diff --git a/g4f/Provider/hf_space/BAAI_Ling.py b/g4f/Provider/hf_space/BAAI_Ling.py new file mode 100644 index 00000000..725fb75a --- /dev/null +++ b/g4f/Provider/hf_space/BAAI_Ling.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import aiohttp +import json +import uuid + +from ...typing import AsyncResult, Messages +from ...providers.response import JsonConversation, Reasoning +from ...requests.raise_for_status import raise_for_status +from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin +from ..helper import format_prompt, get_last_user_message +from ... import debug + +class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): + label = "BAAI Ling" + url = "https://instspace-ling-playground.hf.space" + api_endpoint = f"{url}/gradio_api/queue/join" + + working = True + supports_stream = True + supports_system_message = True + supports_message_history = False + + default_model = "ling-1t" + models = [default_model] + model_aliases = { + "ling-1t": default_model, + "ling": default_model, + } + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + proxy: str = None, + conversation: JsonConversation = None, + **kwargs + ) -> AsyncResult: + if conversation is None or not hasattr(conversation, 'session_hash'): + conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace('-', '')[:12]) + + prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages) + + headers = { + 'accept': '*/*', + 'accept-language': 'en-US,en;q=0.9', + 'content-type': 'application/json', + 'origin': cls.url, + 'referer': f'{cls.url}/', + 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' + } + + payload = { + "data": [prompt], + "event_data": None, + "fn_index": 0, + "trigger_id": 5, + "session_hash": conversation.session_hash + } + + async with aiohttp.ClientSession() as session: + async with session.post(cls.api_endpoint, headers=headers, json=payload, proxy=proxy) as response: + await raise_for_status(response) + await response.json() + + data_url = f'{cls.url}/gradio_api/queue/data?session_hash={conversation.session_hash}' + headers_data = { + 'accept': 'text/event-stream', + 'referer': f'{cls.url}/', + 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' + } + + async with session.get(data_url, headers=headers_data, proxy=proxy) as response: + full_response = "" + async for line in response.content: + decoded_line = line.decode('utf-8') + if decoded_line.startswith('data: '): + try: + json_data = json.loads(decoded_line[6:]) + + if json_data.get('msg') == 'process_generating': + if 'output' in json_data and 'data' in json_data['output']: + output_data = json_data['output']['data'] + if output_data and len(output_data) > 0: + text = output_data[0] + if isinstance(text, str) and text.startswith(full_response): + yield text[len(full_response):] + full_response = text + elif isinstance(text, str): + yield text + full_response = text + + elif json_data.get('msg') == 'process_completed': + if 'output' in json_data and 'data' in json_data['output']: + output_data = json_data['output']['data'] + if output_data and len(output_data) > 0: + final_text = output_data[0] + if isinstance(final_text, str) and len(final_text) > len(full_response): + yield final_text[len(full_response):] + break + + except json.JSONDecodeError: + debug.log("Could not parse JSON:", decoded_line) diff --git a/g4f/Provider/hf_space/__init__.py b/g4f/Provider/hf_space/__init__.py index 150467b0..3ad57263 100644 --- a/g4f/Provider/hf_space/__init__.py +++ b/g4f/Provider/hf_space/__init__.py @@ -6,6 +6,7 @@ from ...typing import AsyncResult, Messages, MediaListType from ...errors import ResponseError from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin +from .BAAI_Ling import BAAI_Ling from .BlackForestLabs_Flux1Dev import BlackForestLabs_Flux1Dev from .BlackForestLabs_Flux1KontextDev import BlackForestLabs_Flux1KontextDev from .CohereForAI_C4AI_Command import CohereForAI_C4AI_Command @@ -27,6 +28,7 @@ class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin): default_image_model = BlackForestLabs_Flux1Dev.default_model default_vision_model = Microsoft_Phi_4_Multimodal.default_model providers = [ + BAAI_Ling, BlackForestLabs_Flux1Dev, BlackForestLabs_Flux1KontextDev, CohereForAI_C4AI_Command, From 04e300d7a6ae4bfaa0edaa71241a59a7457827d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:35:54 +0000 Subject: [PATCH 10/15] Fix code review issues in BAAI_Ling provider Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/hf_space/BAAI_Ling.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/g4f/Provider/hf_space/BAAI_Ling.py b/g4f/Provider/hf_space/BAAI_Ling.py index 725fb75a..dbd1fbb2 100644 --- a/g4f/Provider/hf_space/BAAI_Ling.py +++ b/g4f/Provider/hf_space/BAAI_Ling.py @@ -5,7 +5,7 @@ import json import uuid from ...typing import AsyncResult, Messages -from ...providers.response import JsonConversation, Reasoning +from ...providers.response import JsonConversation from ...requests.raise_for_status import raise_for_status from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin from ..helper import format_prompt, get_last_user_message @@ -22,11 +22,10 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): supports_message_history = False default_model = "ling-1t" - models = [default_model] model_aliases = { - "ling-1t": default_model, "ling": default_model, } + models = [default_model] @classmethod async def create_async_generator( @@ -37,10 +36,11 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): conversation: JsonConversation = None, **kwargs ) -> AsyncResult: - if conversation is None or not hasattr(conversation, 'session_hash'): + is_new_conversation = conversation is None or not hasattr(conversation, 'session_hash') + if is_new_conversation: conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace('-', '')[:12]) - prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages) + prompt = format_prompt(messages) if is_new_conversation else get_last_user_message(messages) headers = { 'accept': '*/*', @@ -62,6 +62,7 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): async with aiohttp.ClientSession() as session: async with session.post(cls.api_endpoint, headers=headers, json=payload, proxy=proxy) as response: await raise_for_status(response) + # Response body must be consumed for the request to complete await response.json() data_url = f'{cls.url}/gradio_api/queue/data?session_hash={conversation.session_hash}' From 098b2401eae4afe654a3934fd80e1c5996433b48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:36:25 +0000 Subject: [PATCH 11/15] Fix response parsing: use type "reply" with data.content/reasoningContent, update models Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/GradientNetwork.py | 38 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py index b6aec90c..e253a4bb 100644 --- a/g4f/Provider/GradientNetwork.py +++ b/g4f/Provider/GradientNetwork.py @@ -11,7 +11,7 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): """ Provider for chat.gradient.network - Supports streaming text generation with various Qwen models. + Supports streaming text generation with Qwen and GPT OSS models. """ label = "Gradient Network" url = "https://chat.gradient.network" @@ -23,18 +23,15 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): supports_system_message = True supports_message_history = True - default_model = "qwen3-235b" + default_model = "Qwen3 235B" models = [ default_model, - "qwen3-32b", - "deepseek-r1-0528", - "deepseek-v3-0324", - "llama-4-maverick", + "GPT OSS 120B", ] model_aliases = { - "qwen-3-235b": "qwen3-235b", - "deepseek-r1": "deepseek-r1-0528", - "deepseek-v3": "deepseek-v3-0324", + "qwen-3-235b": "Qwen3 235B", + "qwen3-235b": "Qwen3 235B", + "gpt-oss-120b": "GPT OSS 120B", } @classmethod @@ -62,7 +59,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): Yields: str: Content chunks from the response - Reasoning: Thinking content when enable_thinking is True + Reasoning: Reasoning content when enable_thinking is True """ model = cls.get_model(model) @@ -101,22 +98,17 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): data = json.loads(line) msg_type = data.get("type") - if msg_type == "text": - # Regular text content - content = data.get("data") + if msg_type == "reply": + # Response chunks with content or reasoningContent + reply_data = data.get("data", {}) + content = reply_data.get("content") + reasoning_content = reply_data.get("reasoningContent") + + if reasoning_content: + yield Reasoning(reasoning_content) if content: yield content - elif msg_type == "thinking": - # Thinking/reasoning content - content = data.get("data") - if content: - yield Reasoning(content) - - elif msg_type == "done": - # Stream complete - break - elif msg_type in ("clusterInfo", "blockUpdate"): # Skip GPU cluster visualization messages continue From 21113c51a64d2e6804cd787e1dd918c2c68e33cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:39:45 +0000 Subject: [PATCH 12/15] Remove redundant continue statement for cluster message handling Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com> --- g4f/Provider/GradientNetwork.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py index e253a4bb..2a7f20a8 100644 --- a/g4f/Provider/GradientNetwork.py +++ b/g4f/Provider/GradientNetwork.py @@ -109,9 +109,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): if content: yield content - elif msg_type in ("clusterInfo", "blockUpdate"): - # Skip GPU cluster visualization messages - continue + # Skip clusterInfo and blockUpdate GPU visualization messages except json.JSONDecodeError: # Skip non-JSON lines (may be partial data or empty) From 1fd9b8d116804d095146e4065f0ad916bb7a89da Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:20:29 +0100 Subject: [PATCH 13/15] Refactor GradientNetwork and ItalyGPT providers; update BAAI_Ling for improved functionality and model handling --- g4f/Provider/GradientNetwork.py | 23 ++++---------- g4f/Provider/ItalyGPT.py | 7 ++-- g4f/Provider/__init__.py | 1 + g4f/Provider/hf_space/BAAI_Ling.py | 51 +++++++++++++++++------------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/g4f/Provider/GradientNetwork.py b/g4f/Provider/GradientNetwork.py index 2a7f20a8..679adfb2 100644 --- a/g4f/Provider/GradientNetwork.py +++ b/g4f/Provider/GradientNetwork.py @@ -3,7 +3,7 @@ from __future__ import annotations import json from ..typing import AsyncResult, Messages -from ..providers.response import Reasoning +from ..providers.response import Reasoning, JsonResponse from ..requests import StreamSession from .base_provider import AsyncGeneratorProvider, ProviderModelMixin @@ -23,7 +23,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): supports_system_message = True supports_message_history = True - default_model = "Qwen3 235B" + default_model = "GPT OSS 120B" models = [ default_model, "GPT OSS 120B", @@ -40,9 +40,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): model: str, messages: Messages, proxy: str = None, - temperature: float = None, - max_tokens: int = None, - enable_thinking: bool = False, + enable_thinking: bool = True, **kwargs ) -> AsyncResult: """ @@ -52,8 +50,6 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): model: The model name to use messages: List of message dictionaries proxy: Optional proxy URL - temperature: Optional temperature parameter - max_tokens: Optional max tokens parameter enable_thinking: Enable the thinking/analysis channel (maps to enableThinking in API) **kwargs: Additional arguments @@ -66,24 +62,18 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): headers = { "Accept": "application/x-ndjson", "Content-Type": "application/json", - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", "Origin": cls.url, "Referer": f"{cls.url}/", } payload = { + "clusterMode": "nvidia" if "GPT OSS" in model else "hybrid", "model": model, "messages": messages, } - - if temperature is not None: - payload["temperature"] = temperature - if max_tokens is not None: - payload["max_tokens"] = max_tokens if enable_thinking: payload["enableThinking"] = enable_thinking - - async with StreamSession(headers=headers, proxy=proxy) as session: + async with StreamSession(headers=headers, proxy=proxy, impersonate="chrome") as session: async with session.post( cls.api_endpoint, json=payload, @@ -96,6 +86,7 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): try: data = json.loads(line) + yield JsonResponse.from_dict(data) msg_type = data.get("type") if msg_type == "reply": @@ -113,4 +104,4 @@ class GradientNetwork(AsyncGeneratorProvider, ProviderModelMixin): except json.JSONDecodeError: # Skip non-JSON lines (may be partial data or empty) - continue + raise diff --git a/g4f/Provider/ItalyGPT.py b/g4f/Provider/ItalyGPT.py index e13fc0ee..43e9eba0 100644 --- a/g4f/Provider/ItalyGPT.py +++ b/g4f/Provider/ItalyGPT.py @@ -1,5 +1,6 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin from ..typing import AsyncResult, Messages +from ..requests import DEFAULT_HEADERS from aiohttp import ClientSession class ItalyGPT(AsyncGeneratorProvider, ProviderModelMixin): @@ -23,10 +24,10 @@ class ItalyGPT(AsyncGeneratorProvider, ProviderModelMixin): ) -> AsyncResult: model = cls.get_model(model) headers = { + **DEFAULT_HEADERS, "content-type": "application/json", "origin": "https://italygpt.it", "referer": "https://italygpt.it/", - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" } payload = { "messages": messages, @@ -34,12 +35,12 @@ class ItalyGPT(AsyncGeneratorProvider, ProviderModelMixin): } async with ClientSession() as session: async with session.post( - f"{cls.url}/api/chat/", + f"{cls.url}/api/chat", json=payload, headers=headers, proxy=proxy, ) as resp: resp.raise_for_status() - async for chunk in resp.content: + async for chunk in resp.content.iter_any(): if chunk: yield chunk.decode() diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index ea51e380..1f97f9ce 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -49,6 +49,7 @@ from .DeepInfra import DeepInfra from .EasyChat import EasyChat from .GLM import GLM from .GradientNetwork import GradientNetwork +from .ItalyGPT import ItalyGPT from .LambdaChat import LambdaChat from .Mintlify import Mintlify from .OIVSCodeSer import OIVSCodeSer2, OIVSCodeSer0501 diff --git a/g4f/Provider/hf_space/BAAI_Ling.py b/g4f/Provider/hf_space/BAAI_Ling.py index dbd1fbb2..cff02dd0 100644 --- a/g4f/Provider/hf_space/BAAI_Ling.py +++ b/g4f/Provider/hf_space/BAAI_Ling.py @@ -8,12 +8,12 @@ from ...typing import AsyncResult, Messages from ...providers.response import JsonConversation from ...requests.raise_for_status import raise_for_status from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin -from ..helper import format_prompt, get_last_user_message +from ..helper import format_prompt, get_last_user_message, get_system_prompt from ... import debug class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): - label = "BAAI Ling" - url = "https://instspace-ling-playground.hf.space" + label = "Ling & Ring Playground" + url = "https://cafe3310-ling-playground.hf.space" api_endpoint = f"{url}/gradio_api/queue/join" working = True @@ -25,7 +25,7 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): model_aliases = { "ling": default_model, } - models = [default_model] + models = ['ling-mini-2.0', 'ling-1t', 'ling-flash-2.0', 'ring-1t', 'ring-flash-2.0', 'ring-mini-2.0'] @classmethod async def create_async_generator( @@ -40,6 +40,7 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): if is_new_conversation: conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace('-', '')[:12]) + model = cls.get_model(model) prompt = format_prompt(messages) if is_new_conversation else get_last_user_message(messages) headers = { @@ -52,10 +53,21 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): } payload = { - "data": [prompt], + "data": [ + prompt, + [ + [ + None, + "Hello! I'm Ling. Try selecting a scenario and a message example below to get started." + ] + ], + get_system_prompt(messages), + 1, + model + ], "event_data": None, - "fn_index": 0, - "trigger_id": 5, + "fn_index": 11, + "trigger_id": 14, "session_hash": conversation.session_hash } @@ -79,27 +91,22 @@ class BAAI_Ling(AsyncGeneratorProvider, ProviderModelMixin): if decoded_line.startswith('data: '): try: json_data = json.loads(decoded_line[6:]) - if json_data.get('msg') == 'process_generating': if 'output' in json_data and 'data' in json_data['output']: output_data = json_data['output']['data'] if output_data and len(output_data) > 0: - text = output_data[0] - if isinstance(text, str) and text.startswith(full_response): - yield text[len(full_response):] - full_response = text - elif isinstance(text, str): - yield text - full_response = text + parts = output_data[0][0] + if len(parts) == 2: + new_text = output_data[0][1].pop() + full_response += new_text + yield new_text + if len(parts) > 2: + new_text = parts[2] + full_response += new_text + yield new_text elif json_data.get('msg') == 'process_completed': - if 'output' in json_data and 'data' in json_data['output']: - output_data = json_data['output']['data'] - if output_data and len(output_data) > 0: - final_text = output_data[0] - if isinstance(final_text, str) and len(final_text) > len(full_response): - yield final_text[len(full_response):] - break + break except json.JSONDecodeError: debug.log("Could not parse JSON:", decoded_line) From 468dc7bd671306fb01f34b6ebc0525c0e4ede66b Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Sun, 30 Nov 2025 22:30:14 +0100 Subject: [PATCH 14/15] Add "gemini-3-pro-preview" model to GeminiCLI provider --- g4f/Provider/needs_auth/GeminiCLI.py | 1 + 1 file changed, 1 insertion(+) diff --git a/g4f/Provider/needs_auth/GeminiCLI.py b/g4f/Provider/needs_auth/GeminiCLI.py index 8797a1f4..09aec55c 100644 --- a/g4f/Provider/needs_auth/GeminiCLI.py +++ b/g4f/Provider/needs_auth/GeminiCLI.py @@ -500,6 +500,7 @@ class GeminiCLI(AsyncGeneratorProvider, ProviderModelMixin): models = [ "gemini-2.5-pro", "gemini-2.5-flash", + "gemini-3-pro-preview" ] working = True From 957d73a76ece5284c1920431bfccea7edf96a502 Mon Sep 17 00:00:00 2001 From: hlohaus <983577+hlohaus@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:34:10 +0100 Subject: [PATCH 15/15] Add UTF-8 encoding to file writes in Backend_Api class --- g4f/gui/server/backend_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/g4f/gui/server/backend_api.py b/g4f/gui/server/backend_api.py index 2d509a06..a391349d 100644 --- a/g4f/gui/server/backend_api.py +++ b/g4f/gui/server/backend_api.py @@ -440,7 +440,7 @@ class Backend_Api(Api): os.remove(copyfile) continue if not is_media and result: - with open(os.path.join(bucket_dir, f"{filename}.md"), 'w') as f: + with open(os.path.join(bucket_dir, f"{filename}.md"), 'w', encoding="utf-8") as f: f.write(f"{result}\n") filenames.append(f"{filename}.md") if is_media: @@ -477,7 +477,7 @@ class Backend_Api(Api): except OSError: shutil.copyfile(copyfile, newfile) os.remove(copyfile) - with open(os.path.join(bucket_dir, "files.txt"), 'w') as f: + with open(os.path.join(bucket_dir, "files.txt"), 'w', encoding="utf-8") as f: for filename in filenames: f.write(f"{filename}\n") return {"bucket_id": bucket_id, "files": filenames, "media": media} @@ -572,7 +572,7 @@ class Backend_Api(Api): share_id = secure_filename(share_id) bucket_dir = get_bucket_dir(share_id) os.makedirs(bucket_dir, exist_ok=True) - with open(os.path.join(bucket_dir, "chat.json"), 'w') as f: + with open(os.path.join(bucket_dir, "chat.json"), 'w', encoding="utf-8") as f: json.dump(chat_data, f) self.chat_cache[share_id] = updated return {"share_id": share_id}