diff --git a/g4f/Provider/DDG.py b/g4f/Provider/DDG.py
index 7f9dc5b5..a3d7510a 100644
--- a/g4f/Provider/DDG.py
+++ b/g4f/Provider/DDG.py
@@ -6,15 +6,14 @@ import time
import random
import hashlib
import asyncio
-from datetime import datetime
from aiohttp import ClientSession
from ..typing import AsyncResult, Messages
+from ..errors import ResponseError
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt, get_last_user_message
from ..providers.response import FinishReason, JsonConversation
-
class Conversation(JsonConversation):
message_history: Messages = []
@@ -22,7 +21,6 @@ class Conversation(JsonConversation):
self.model = model
self.message_history = []
-
class DDG(AsyncGeneratorProvider, ProviderModelMixin):
label = "DuckDuckGo AI Chat"
url = "https://duckduckgo.com"
@@ -161,8 +159,7 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin):
if response.status != 200:
error_text = await response.text()
if "ERR_BN_LIMIT" in error_text:
- yield "Blocked by DuckDuckGo: Bot limit exceeded (ERR_BN_LIMIT)."
- return
+ raise ResponseError("Blocked by DuckDuckGo: Bot limit exceeded (ERR_BN_LIMIT).")
if "ERR_INVALID_VQD" in error_text and retry_count < 3:
await asyncio.sleep(random.uniform(2.5, 5.5))
async for chunk in cls.create_async_generator(
@@ -170,9 +167,7 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin):
):
yield chunk
return
- yield f"Error: HTTP {response.status} - {error_text}"
- return
-
+ raise ResponseError(f"HTTP {response.status} - {error_text}")
full_message = ""
async for line in response.content:
line_text = line.decode("utf-8").strip()
@@ -188,8 +183,7 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin):
try:
msg = json.loads(payload)
if msg.get("action") == "error":
- yield f"Error: {msg.get('type', 'unknown')}"
- break
+ raise ResponseError(f"Error: {msg.get('type', 'unknown')}")
if "message" in msg:
content = msg["message"]
yield content
@@ -204,4 +198,4 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin):
):
yield chunk
else:
- yield f"Error: {str(e)}"
+ raise ResponseError(f"Error: {str(e)}")
diff --git a/g4f/Provider/DeepInfraChat.py b/g4f/Provider/DeepInfraChat.py
index 1f6caf69..8eca02c6 100644
--- a/g4f/Provider/DeepInfraChat.py
+++ b/g4f/Provider/DeepInfraChat.py
@@ -45,10 +45,10 @@ class DeepInfraChat(OpenaiTemplate):
] + vision_models
model_aliases = {
"deepseek-prover-v2-671b": "deepseek-ai/DeepSeek-Prover-V2-671B",
- "qwen-3-235b": "Qwen/Qwen3-235B-A22B",
- "qwen-3-30b": "Qwen/Qwen3-30B-A3B",
- "qwen-3-32b": "Qwen/Qwen3-32B",
- "qwen-3-14b": "Qwen/Qwen3-14B",
+ "qwen3-235b": "Qwen/Qwen3-235B-A22B",
+ "qwen3-30b": "Qwen/Qwen3-30B-A3B",
+ "qwen3-32b": "Qwen/Qwen3-32B",
+ "qwen3-14b": "Qwen/Qwen3-14B",
"llama-4-maverick": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
"llama-4-maverick-17b": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
"llama-4-scout": "meta-llama/Llama-4-Scout-17B-16E-Instruct",
diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py
index 28260568..12b9fd0f 100644
--- a/g4f/Provider/PollinationsAI.py
+++ b/g4f/Provider/PollinationsAI.py
@@ -254,8 +254,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
enhance=enhance,
safe=safe,
n=n,
- referrer=referrer,
- extra_body=extra_body
+ referrer=referrer
):
yield chunk
else:
@@ -305,10 +304,9 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
enhance: bool,
safe: bool,
n: int,
- referrer: str,
- extra_body: dict
+ referrer: str
) -> AsyncResult:
- extra_body = use_aspect_ratio({
+ params = use_aspect_ratio({
"width": width,
"height": height,
"model": model,
@@ -316,9 +314,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
"private": str(private).lower(),
"enhance": str(enhance).lower(),
"safe": str(safe).lower(),
- **extra_body
}, aspect_ratio)
- query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in extra_body.items() if v is not None)
+ query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
prompt = quote_plus(prompt)[:2048-len(cls.image_api_endpoint)-len(query)-8]
url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
def get_image_url(i: int, seed: Optional[int] = None):
diff --git a/g4f/Provider/PollinationsImage.py b/g4f/Provider/PollinationsImage.py
index 5d50fc97..c9251f6a 100644
--- a/g4f/Provider/PollinationsImage.py
+++ b/g4f/Provider/PollinationsImage.py
@@ -37,6 +37,7 @@ class PollinationsImage(PollinationsAI):
model: str,
messages: Messages,
proxy: str = None,
+ referrer: str = "https://gpt4free.github.io/",
prompt: str = None,
aspect_ratio: str = "1:1",
width: int = None,
@@ -65,6 +66,7 @@ class PollinationsImage(PollinationsAI):
private=private,
enhance=enhance,
safe=safe,
- n=n
+ n=n,
+ referrer=referrer
):
yield chunk
diff --git a/g4f/Provider/har/__init__.py b/g4f/Provider/har/__init__.py
index 54828d41..ddbb5930 100644
--- a/g4f/Provider/har/__init__.py
+++ b/g4f/Provider/har/__init__.py
@@ -5,8 +5,11 @@ import json
import uuid
from urllib.parse import urlparse
-from ...typing import AsyncResult, Messages
-from ...requests import StreamSession, raise_for_status
+from ...typing import AsyncResult, Messages, MediaListType
+from ...requests import StreamSession, StreamResponse, FormData, raise_for_status
+from ...providers.response import JsonConversation
+from ...tools.media import merge_media
+from ...image import to_bytes, is_accepted_format
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..helper import get_last_user_message
from ..openai.har_file import get_headers
@@ -14,8 +17,54 @@ from ..openai.har_file import get_headers
class HarProvider(AsyncGeneratorProvider, ProviderModelMixin):
label = "LM Arena"
url = "https://lmarena.ai"
+ api_endpoint = "/queue/join?"
working = True
default_model = "chatgpt-4o-latest-20250326"
+ model_aliases = {
+ "claude-3.7-sonnet": "claude-3-7-sonnet-20250219",
+ }
+ vision_models = [
+ "o3-2025-04-16",
+ "o4-mini-2025-04-16",
+ "gpt-4.1-2025-04-14",
+ "gemini-2.5-pro-exp-03-25",
+ "claude-3-7-sonnet-20250219",
+ "claude-3-7-sonnet-20250219-thinking-32k",
+ "llama-4-maverick-17b-128e-instruct",
+ "gpt-4.1-mini-2025-04-14",
+ "gpt-4.1-nano-2025-04-14",
+ "gemini-2.0-flash-thinking-exp-01-21",
+ "gemini-2.0-flash-001",
+ "gemini-2.0-flash-lite-preview-02-05",
+ "claude-3-5-sonnet-20241022",
+ "gpt-4o-mini-2024-07-18",
+ "gpt-4o-2024-11-20",
+ "gpt-4o-2024-08-06",
+ "gpt-4o-2024-05-13",
+ "claude-3-5-sonnet-20240620",
+ "doubao-1.5-vision-pro-32k-250115",
+ "amazon-nova-pro-v1.0",
+ "amazon-nova-lite-v1.0",
+ "qwen2.5-vl-32b-instruct",
+ "qwen2.5-vl-72b-instruct",
+ "gemini-1.5-pro-002",
+ "gemini-1.5-flash-002",
+ "gemini-1.5-flash-8b-001",
+ "gemini-1.5-pro-001",
+ "gemini-1.5-flash-001",
+ "hunyuan-standard-vision-2024-12-31",
+ "pixtral-large-2411",
+ "step-1o-vision-32k-highres",
+ "claude-3-haiku-20240307",
+ "claude-3-sonnet-20240229",
+ "claude-3-opus-20240229",
+ "qwen-vl-max-1119",
+ "qwen-vl-max-0809",
+ "reka-core-20240904",
+ "reka-flash-20240904",
+ "c4ai-aya-vision-32b",
+ "pixtral-12b-2409"
+ ]
@classmethod
def get_models(cls):
@@ -33,51 +82,141 @@ class HarProvider(AsyncGeneratorProvider, ProviderModelMixin):
break
return cls.models
+ @classmethod
+ def _build_second_payloads(cls, model_id: str, session_hash: str, text: str, max_tokens: int, temperature: float, top_p: float):
+ first_payload = {
+ "data":[None,model_id,text,{
+ "text_models":[model_id],
+ "all_text_models":[model_id],
+ "vision_models":[],
+ "image_gen_models":[],
+ "all_image_gen_models":[],
+ "search_models":[],
+ "all_search_models":[],
+ "models":[model_id],
+ "all_models":[model_id],
+ "arena_type":"text-arena"}],
+ "event_data": None,
+ "fn_index": 122,
+ "trigger_id": 157,
+ "session_hash": session_hash
+ }
+
+ second_payload = {
+ "data": [],
+ "event_data": None,
+ "fn_index": 123,
+ "trigger_id": 157,
+ "session_hash": session_hash
+ }
+
+ third_payload = {
+ "data": [None, temperature, top_p, max_tokens],
+ "event_data": None,
+ "fn_index": 124,
+ "trigger_id": 157,
+ "session_hash": session_hash
+ }
+
+ return first_payload, second_payload, third_payload
+
@classmethod
async def create_async_generator(
- cls, model: str, messages: Messages,
+ cls,
+ model: str,
+ messages: Messages,
proxy: str = None,
+ media: MediaListType = None,
+ max_tokens: int = 2048,
+ temperature: float = 0.7,
+ top_p: float = 1,
+ conversation: JsonConversation = None,
**kwargs
) -> AsyncResult:
+ async def read_response(response: StreamResponse):
+ returned_data = ""
+ async for line in response.iter_lines():
+ if not line.startswith(b"data: "):
+ continue
+ for content in find_str(json.loads(line[6:]), 3):
+ if content == ' ' or content == 'update':
+ continue
+ if content.endswith("▌"):
+ content = content[:-2]
+ new_content = content
+ if content.startswith(returned_data):
+ new_content = content[len(returned_data):]
+ if not new_content:
+ continue
+ returned_data += new_content
+ yield new_content
if model in cls.model_aliases:
model = cls.model_aliases[model]
- session_hash = str(uuid.uuid4()).replace("-", "")
prompt = get_last_user_message(messages)
-
- for domain, harFile in read_har_files():
- async with StreamSession(impersonate="chrome") as session:
- for v in harFile['log']['entries']:
- request_url = v['request']['url']
- if domain not in request_url or "." in urlparse(request_url).path or "heartbeat" in request_url:
- continue
- postData = None
- if "postData" in v['request']:
- postData = v['request']['postData']['text']
- postData = postData.replace('"hello"', json.dumps(prompt))
- postData = postData.replace("__SESSION__", session_hash)
- if model:
- postData = postData.replace("__MODEL__", model)
- request_url = request_url.replace("__SESSION__", session_hash)
- method = v['request']['method'].lower()
-
- async with getattr(session, method)(request_url, data=postData, headers=get_headers(v), proxy=proxy) as response:
+ async with StreamSession(impersonate="chrome") as session:
+ if conversation is None:
+ conversation = JsonConversation(session_hash=str(uuid.uuid4()).replace("-", ""))
+ media = list(merge_media(media, messages))
+ if media:
+ data = FormData()
+ for i in range(len(media)):
+ media[i] = (to_bytes(media[i][0]), media[i][1])
+ for image, image_name in media:
+ data.add_field(f"files", image, filename=image_name)
+ async with session.post(f"{cls.url}/upload", params={"upload_id": conversation.session_hash}, data=data) as response:
await raise_for_status(response)
- returned_data = ""
- async for line in response.iter_lines():
- if not line.startswith(b"data: "):
- continue
- for content in find_str(json.loads(line[6:]), 3):
- if content == ' ' or content == 'update':
- continue
- if content.endswith("▌"):
- content = content[:-2]
- new_content = content
- if content.startswith(returned_data):
- new_content = content[len(returned_data):]
- if not new_content:
- continue
- returned_data += new_content
- yield new_content
+ image_files = await response.json()
+ media = [{
+ "path": image_file,
+ "url": f"{cls.url}/file={image_file}",
+ "orig_name": media[i][1],
+ "size": len(media[i][0]),
+ "mime_type": is_accepted_format(media[i][0]),
+ "meta": {
+ "_type": "gradio.FileData"
+ }
+ } for i, image_file in enumerate(image_files)]
+ for domain, harFile in read_har_files():
+ for v in harFile['log']['entries']:
+ request_url = v['request']['url']
+ if domain not in request_url or "." in urlparse(request_url).path or "heartbeat" in request_url:
+ continue
+ postData = None
+ if "postData" in v['request']:
+ postData = v['request']['postData']['text']
+ postData = postData.replace('"hello"', json.dumps(prompt))
+ postData = postData.replace('[null,0.7,1,2048]', json.dumps([None, temperature, top_p, max_tokens]))
+ postData = postData.replace('"files":[]', f'"files":{json.dumps(media)}')
+ postData = postData.replace("__SESSION__", conversation.session_hash)
+ if model:
+ postData = postData.replace("__MODEL__", model)
+ request_url = request_url.replace("__SESSION__", conversation.session_hash)
+ method = v['request']['method'].lower()
+ async with getattr(session, method)(request_url, data=postData, headers=get_headers(v), proxy=proxy) as response:
+ await raise_for_status(response)
+ async for chunk in read_response(response):
+ yield chunk
+ yield conversation
+ else:
+ first_payload, second_payload, third_payload = cls._build_second_payloads(model, conversation.session_hash, prompt, max_tokens, temperature, top_p)
+ headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+ # POST 1
+ async with session.post(f"{cls.url}{cls.api_endpoint}", json=first_payload, proxy=proxy, headers=headers) as response:
+ await raise_for_status(response)
+ # POST 2
+ async with session.post(f"{cls.url}{cls.api_endpoint}", json=second_payload, proxy=proxy, headers=headers) as response:
+ await raise_for_status(response)
+ # POST 3
+ async with session.post(f"{cls.url}{cls.api_endpoint}", json=third_payload, proxy=proxy, headers=headers) as response:
+ await raise_for_status(response)
+ stream_url = f"{cls.url}/queue/data?session_hash={conversation.session_hash}"
+ async with session.get(stream_url, headers={"Accept": "text/event-stream"}, proxy=proxy) as response:
+ await raise_for_status(response)
+ async for chunk in read_response(response):
+ yield chunk
def read_har_files():
for root, _, files in os.walk(os.path.dirname(__file__)):
diff --git a/g4f/Provider/hf/HuggingFaceMedia.py b/g4f/Provider/hf/HuggingFaceMedia.py
index 8f674f8e..60df5fcd 100644
--- a/g4f/Provider/hf/HuggingFaceMedia.py
+++ b/g4f/Provider/hf/HuggingFaceMedia.py
@@ -129,6 +129,8 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
if key in ["replicate", "together", "hf-inference"]
}
provider_mapping = {**new_mapping, **provider_mapping}
+ if not provider_mapping:
+ raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__}")
async def generate(extra_body: dict, aspect_ratio: str = None):
last_response = None
for provider_key, provider in provider_mapping.items():
diff --git a/g4f/Provider/hf/models.py b/g4f/Provider/hf/models.py
index cbd46020..197c4c70 100644
--- a/g4f/Provider/hf/models.py
+++ b/g4f/Provider/hf/models.py
@@ -39,7 +39,7 @@ model_aliases = {
"gemma-2-27b": "google/gemma-2-27b-it",
"qwen-2-72b": "Qwen/Qwen2-72B-Instruct",
"qvq-72b": "Qwen/QVQ-72B-Preview",
- "sd-3.5": "stabilityai/stable-diffusion-3.5-large",
+ "stable-diffusion-3.5-large": "stabilityai/stable-diffusion-3.5-large",
}
extra_models = [
"meta-llama/Llama-3.2-11B-Vision-Instruct",
diff --git a/g4f/Provider/hf_space/DeepseekAI_JanusPro7b.py b/g4f/Provider/hf_space/DeepseekAI_JanusPro7b.py
index e8fbaecf..64896fa5 100644
--- a/g4f/Provider/hf_space/DeepseekAI_JanusPro7b.py
+++ b/g4f/Provider/hf_space/DeepseekAI_JanusPro7b.py
@@ -121,7 +121,7 @@ class DeepseekAI_JanusPro7b(AsyncGeneratorProvider, ProviderModelMixin):
}
} for i, image_file in enumerate(image_files)]
- async with cls.run(method, session, prompt, conversation, None if media is None else media.pop(), seed) as response:
+ async with cls.run(method, session, prompt, conversation, None if not media else media.pop(), seed) as response:
await raise_for_status(response)
async with cls.run("get", session, prompt, conversation, None, seed) as response:
diff --git a/g4f/Provider/hf_space/Qwen_Qwen_3.py b/g4f/Provider/hf_space/Qwen_Qwen_3.py
index 62df738c..9751d6fe 100644
--- a/g4f/Provider/hf_space/Qwen_Qwen_3.py
+++ b/g4f/Provider/hf_space/Qwen_Qwen_3.py
@@ -32,15 +32,6 @@ class Qwen_Qwen_3(AsyncGeneratorProvider, ProviderModelMixin):
"qwen3-1.7b",
"qwen3-0.6b",
}
- model_aliases = {
- "qwen-3-235b": default_model,
- "qwen-3-32b": "qwen3-32b",
- "qwen-3-30b": "qwen3-30b-a3b",
- "qwen-3-14b": "qwen3-14b",
- "qwen-3-4b": "qwen3-4b",
- "qwen-3-1.7b": "qwen3-1.7b",
- "qwen-3-0.6b": "qwen3-0.6b",
- }
@classmethod
async def create_async_generator(
diff --git a/g4f/Provider/hf_space/StabilityAI_SD35Large.py b/g4f/Provider/hf_space/StabilityAI_SD35Large.py
index 26076e2e..9d0239f5 100644
--- a/g4f/Provider/hf_space/StabilityAI_SD35Large.py
+++ b/g4f/Provider/hf_space/StabilityAI_SD35Large.py
@@ -19,7 +19,7 @@ class StabilityAI_SD35Large(AsyncGeneratorProvider, ProviderModelMixin):
default_model = 'stabilityai-stable-diffusion-3-5-large'
default_image_model = default_model
- model_aliases = {"sd-3.5": default_model}
+ model_aliases = {"stable-diffusion-3.5-large": default_model}
image_models = list(model_aliases.keys())
models = image_models
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index 8e45dbc0..121db9ba 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -143,11 +143,10 @@ class Api:
"messages": messages,
"stream": True,
"ignore_stream": True,
- "return_conversation": True,
**kwargs
}
- def _create_response_stream(self, kwargs: dict, conversation_id: str, provider: str, download_media: bool = True) -> Iterator:
+ def _create_response_stream(self, kwargs: dict, provider: str, download_media: bool = True) -> Iterator:
def decorated_log(text: str, file = None):
debug.logs.append(text)
if debug.logging:
@@ -178,12 +177,9 @@ class Api:
for chunk in result:
if isinstance(chunk, ProviderInfo):
yield self.handle_provider(chunk, model)
- provider = chunk.name
elif isinstance(chunk, JsonConversation):
if provider is not None:
- if hasattr(provider, "__name__"):
- provider = provider.__name__
- yield self._format_json("conversation", {
+ yield self._format_json("conversation", chunk.get_dict() if provider == "AnyProvider" else {
provider: chunk.get_dict()
})
elif isinstance(chunk, Exception):
diff --git a/g4f/gui/server/backend_api.py b/g4f/gui/server/backend_api.py
index 367decca..635460b2 100644
--- a/g4f/gui/server/backend_api.py
+++ b/g4f/gui/server/backend_api.py
@@ -128,7 +128,6 @@ class Backend_Api(Api):
return self.app.response_class(
self._create_response_stream(
kwargs,
- json_data.get("conversation_id"),
json_data.get("provider"),
json_data.get("download_media", True),
),
diff --git a/g4f/gui/server/website.py b/g4f/gui/server/website.py
index 1d056a54..90e98e6d 100644
--- a/g4f/gui/server/website.py
+++ b/g4f/gui/server/website.py
@@ -3,8 +3,9 @@ from __future__ import annotations
import os
import requests
from datetime import datetime
-from flask import send_from_directory, redirect, request
-from ...image.copy_images import secure_filename, get_media_dir, ensure_media_dir
+from flask import send_from_directory, redirect
+from ...image.copy_images import secure_filename
+from ...cookies import get_cookies_dir
from ...errors import VersionNotFoundError
from ... import version
@@ -15,27 +16,26 @@ def redirect_home():
return redirect('/chat')
def render(filename = "chat", add_origion = True):
- if request.args.get("live"):
+ if os.path.exists(DIST_DIR):
add_origion = False
- if os.path.exists(DIST_DIR):
- path = os.path.abspath(os.path.join(os.path.dirname(DIST_DIR), (filename + ("" if "." in filename else ".html"))))
- print( f"Debug mode: {path}")
- return send_from_directory(os.path.dirname(path), os.path.basename(path))
+ path = os.path.abspath(os.path.join(os.path.dirname(DIST_DIR), (filename + ("" if "." in filename else ".html"))))
+ return send_from_directory(os.path.dirname(path), os.path.basename(path))
try:
latest_version = version.utils.latest_version
except VersionNotFoundError:
latest_version = version.utils.current_version
today = datetime.today().strftime('%Y-%m-%d')
- cache_file = os.path.join(get_media_dir(), f"{today}.{secure_filename(filename)}.{version.utils.current_version}-{latest_version}{'.live' if add_origion else ''}.html")
+ cache_dir = os.path.join(get_cookies_dir(), ".gui_cache")
+ cache_file = os.path.join(cache_dir, f"{today}.{secure_filename(filename)}.{version.utils.current_version}-{latest_version}{'.live' if add_origion else ''}.html")
if not os.path.exists(cache_file):
- ensure_media_dir()
+ os.makedirs(cache_dir, exist_ok=True)
html = requests.get(f"{GPT4FREE_URL}/{filename}.html").text
if add_origion:
html = html.replace("../dist/", f"dist/")
html = html.replace("\"dist/", f"\"{GPT4FREE_URL}/dist/")
with open(cache_file, 'w', encoding='utf-8') as f:
f.write(html)
- return send_from_directory(os.path.abspath(get_media_dir()), os.path.basename(cache_file))
+ return send_from_directory(os.path.abspath(cache_dir), os.path.basename(cache_file))
class Website:
def __init__(self, app) -> None:
diff --git a/g4f/models.py b/g4f/models.py
index 9d5fed38..a5d7c63d 100644
--- a/g4f/models.py
+++ b/g4f/models.py
@@ -949,7 +949,7 @@ sdxl_turbo = ImageModel(
)
sd_3_5 = ImageModel(
- name = 'sd-3.5',
+ name = 'stable-diffusion-3.5-large',
base_provider = 'Stability AI',
best_provider = HuggingSpace
)
diff --git a/g4f/providers/any_provider.py b/g4f/providers/any_provider.py
index 5c880c9f..e035f14a 100644
--- a/g4f/providers/any_provider.py
+++ b/g4f/providers/any_provider.py
@@ -5,7 +5,6 @@ from ..errors import ModelNotFoundError
from ..image import is_data_an_audio
from ..providers.retry_provider import IterListProvider
from ..providers.types import ProviderType
-from ..providers.response import JsonConversation, ProviderInfo
from ..Provider.needs_auth import OpenaiChat, CopilotAccount
from ..Provider.hf_space import HuggingSpace
from ..Provider import Cloudflare, Gemini, Grok, DeepSeekAPI, PerplexityLabs, LambdaChat, PollinationsAI, FreeRouter
@@ -14,9 +13,9 @@ from ..Provider import HarProvider, DDG, HuggingFace, HuggingFaceMedia
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .. import Provider
from .. import models
-from .. import debug
LABELS = {
+ "default": "Default",
"openai": "OpenAI: ChatGPT",
"llama": "Meta: LLaMA",
"deepseek": "DeepSeek",
@@ -26,6 +25,7 @@ LABELS = {
"claude": "Anthropic: Claude",
"command": "Cohere: Command",
"phi": "Microsoft: Phi",
+ "mistral": "Mistral",
"PollinationsAI": "Pollinations AI",
"perplexity": "Perplexity Labs",
"video": "Video Generation",
@@ -45,7 +45,12 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
for model in unsorted_models:
added = False
for group in groups:
- if group == "qwen":
+ if group == "mistral":
+ if model.split("-")[0] in ("mistral", "mixtral", "mistralai", "pixtral", "ministral", "codestral"):
+ groups[group].append(model)
+ added = True
+ break
+ elif group == "qwen":
if model.startswith("qwen") or model.startswith("qwq") or model.startswith("qvq"):
groups[group].append(model)
added = True
@@ -198,8 +203,6 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
stream: bool = True,
media: MediaListType = None,
ignored: list[str] = [],
- conversation: JsonConversation = None,
- api_key: str = None,
**kwargs
) -> AsyncResult:
cls.get_models(ignored=ignored)
@@ -246,33 +249,7 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
providers = list({provider.__name__: provider for provider in providers}.values())
if len(providers) == 0:
raise ModelNotFoundError(f"Model {model} not found in any provider.")
- if len(providers) == 1:
- provider = providers[0]
- if conversation is not None:
- child_conversation = getattr(conversation, provider.__name__, None)
- if child_conversation is not None:
- kwargs["conversation"] = JsonConversation(**child_conversation)
- debug.log(f"Using {provider.__name__} provider" + f" and {model} model" if model else "")
- yield ProviderInfo(**provider.get_dict(), model=model)
- if provider in (HuggingFace, HuggingFaceMedia):
- kwargs["api_key"] = api_key
- async for chunk in provider.get_async_create_function()(
- model,
- messages,
- stream=stream,
- media=media,
- **kwargs
- ):
- if isinstance(chunk, JsonConversation):
- if conversation is None:
- conversation = JsonConversation()
- setattr(conversation, provider.__name__, chunk.get_dict())
- yield conversation
- else:
- yield chunk
- return
- kwargs["api_key"] = api_key
- async for chunk in IterListProvider(providers).get_async_create_function()(
+ async for chunk in IterListProvider(providers).create_async_generator(
model,
messages,
stream=stream,
diff --git a/g4f/providers/retry_provider.py b/g4f/providers/retry_provider.py
index 3c6beb88..b128e7c9 100644
--- a/g4f/providers/retry_provider.py
+++ b/g4f/providers/retry_provider.py
@@ -4,7 +4,7 @@ import random
from ..typing import Type, List, CreateResult, Messages, AsyncResult
from .types import BaseProvider, BaseRetryProvider, ProviderType
-from .response import MediaResponse, AudioResponse, ProviderInfo, Reasoning
+from .response import MediaResponse, AudioResponse, ProviderInfo, Reasoning, JsonConversation
from .. import debug
from ..errors import RetryProviderError, RetryNoProviderError, MissingAuthError, NoValidHarFileError
@@ -38,7 +38,6 @@ class IterListProvider(BaseRetryProvider):
stream: bool = False,
ignore_stream: bool = False,
ignored: list[str] = [],
- api_key: str = None,
**kwargs,
) -> CreateResult:
"""
@@ -59,8 +58,6 @@ class IterListProvider(BaseRetryProvider):
self.last_provider = provider
debug.log(f"Using {provider.__name__} provider")
yield ProviderInfo(**provider.get_dict(), model=model if model else getattr(provider, "default_model"))
- if self.add_api_key or provider.__name__ in ["HuggingFace", "HuggingFaceMedia"]:
- kwargs["api_key"] = api_key
try:
response = provider.get_create_function()(model, messages, stream=stream, **kwargs)
for chunk in response:
@@ -86,6 +83,8 @@ class IterListProvider(BaseRetryProvider):
stream: bool = True,
ignore_stream: bool = False,
ignored: list[str] = [],
+ api_key: str = None,
+ conversation: JsonConversation = None,
**kwargs
) -> AsyncResult:
exceptions = {}
@@ -93,13 +92,23 @@ class IterListProvider(BaseRetryProvider):
for provider in self.get_providers(stream and not ignore_stream, ignored):
self.last_provider = provider
- debug.log(f"Using {provider.__name__} provider")
+ debug.log(f"Using {provider.__name__} provider and {model} model")
yield ProviderInfo(**provider.get_dict(), model=model if model else getattr(provider, "default_model"))
+ extra_body = kwargs.copy()
+ if self.add_api_key or provider.__name__ in ["HuggingFace", "HuggingFaceMedia"]:
+ extra_body["api_key"] = api_key
+ if conversation is not None and hasattr(conversation, provider.__name__):
+ extra_body["conversation"] = JsonConversation(**getattr(conversation, provider.__name__))
try:
- response = provider.get_async_create_function()(model, messages, stream=stream, **kwargs)
+ response = provider.get_async_create_function()(model, messages, stream=stream, **extra_body)
if hasattr(response, "__aiter__"):
async for chunk in response:
- if chunk:
+ if isinstance(chunk, JsonConversation):
+ if conversation is None:
+ conversation = JsonConversation()
+ setattr(conversation, provider.__name__, chunk.get_dict())
+ yield conversation
+ elif chunk:
yield chunk
if is_content(chunk):
started = True
@@ -246,6 +255,8 @@ def raise_exceptions(exceptions: dict) -> None:
for provider_name, e in exceptions.items():
if isinstance(e, (MissingAuthError, NoValidHarFileError)):
raise e
+ if len(exceptions) == 1:
+ raise list(exceptions.values())[0]
raise RetryProviderError("RetryProvider failed:\n" + "\n".join([
f"{p}: {type(exception).__name__}: {exception}" for p, exception in exceptions.items()
])) from list(exceptions.values())[0]
diff --git a/g4f/tools/files.py b/g4f/tools/files.py
index 7516822c..6948b5b0 100644
--- a/g4f/tools/files.py
+++ b/g4f/tools/files.py
@@ -189,7 +189,7 @@ def stream_read_files(bucket_dir: Path, filenames: list, delete_files: bool = Fa
else:
os.unlink(filepath)
continue
- yield f"```{filename}\n"
+ yield f"```{filename.replace('.md', '')}\n"
if has_pypdf2 and filename.endswith(".pdf"):
try:
reader = PyPDF2.PdfReader(file_path)