返回提交历史
Modified
g4f/Provider/PollinationsAI.py
+8
-6
Modified
g4f/Provider/__init__.py
+1
-1
Modified
g4f/Provider/hf/HuggingChat.py
+4
-3
Modified
g4f/Provider/hf/HuggingFaceAPI.py
+16
-10
Modified
g4f/Provider/hf/HuggingFaceInference.py
+5
-6
Added
g4f/Provider/hf/HuggingFaceMedia.py
+175
-0
Modified
g4f/Provider/hf/__init__.py
+7
-0
Modified
g4f/Provider/hf/models.py
+2
-6
Modified
g4f/Provider/hf_space/BlackForestLabs_Flux1Dev.py
+0
-1
Modified
g4f/Provider/hf_space/BlackForestLabs_Flux1Schnell.py
+0
-2
Modified
g4f/Provider/hf_space/CohereForAI_C4AI_Command.py
+6
-1
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+15
-1
Modified
g4f/api/__init__.py
+3
-3
Modified
g4f/client/__init__.py
+2
-2
Modified
g4f/gui/client/qrcode.html
+2
-3
Modified
g4f/gui/client/static/js/chat.v1.js
+11
-10
Modified
g4f/gui/server/api.py
+12
-11
Modified
g4f/gui/server/backend_api.py
+21
-14
Modified
g4f/image/copy_images.py
+53
-30
Modified
g4f/models.py
+0
-6
Modified
g4f/requests/raise_for_status.py
+13
-12
XFEstudio/gpt4free
Add HuggingFaceMedia provider with Video Generation Add Support for Video Response in UI Improve Support for Audio Response in UI Fix ModelNotSupported errors in HuggingSpace providers
8eaaf5db
代码差异
21 个文件
+356
-128
@@ -14,7 +14,8 @@ from ..image import to_data_uri, is_data_an_audio, to_input_audio
14
14
from ..errors import ModelNotFoundError
15
15
from ..requests.raise_for_status import raise_for_status
16
16
from ..requests.aiohttp import get_connector
17
from ..providers.response import ImageResponse, ImagePreview, FinishReason, Usage, Audio, ToolCalls
17
from ..image.copy_images import save_response_media
18
from ..providers.response import FinishReason, Usage, ToolCalls
18
19
from .. import debug
19
20
20
21
DEFAULT_HEADERS = {
@@ -239,8 +240,9 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
239
240
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
240
241
async with session.get(url, allow_redirects=True) as response:
241
242
await raise_for_status(response)
242
image_url = str(response.url)
243
yield ImageResponse(image_url, prompt)
243
async for chunk in save_response_media(response, prompt):
244
yield chunk
245
return
244
246
245
247
@classmethod
246
248
async def _generate_text(
@@ -305,10 +307,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
305
307
})
306
308
async with session.post(url, json=data) as response:
307
309
await raise_for_status(response)
308
if response.headers["content-type"] == "audio/mpeg":
309
yield Audio(await response.read())
310
async for chunk in save_response_media(response, messages[-1]["content"]):
311
yield chunk
310
312
return
311
elif response.headers["content-type"].startswith("text/plain"):
313
if response.headers["content-type"].startswith("text/plain"):
312
314
yield await response.text()
313
315
return
314
316
elif response.headers["content-type"].startswith("text/event-stream"):
@@ -9,7 +9,7 @@ from .deprecated import *
9
9
from .needs_auth import *
10
10
from .not_working import *
11
11
from .local import *
12
from .hf import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference
12
from .hf import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference, HuggingFaceMedia
13
13
from .hf_space import *
14
14
from .mini_max import HailuoAI, MiniMax
15
15
from .template import OpenaiTemplate, BackendApi
@@ -24,7 +24,7 @@ from ...requests import get_args_from_nodriver, DEFAULT_HEADERS
24
24
from ...requests.raise_for_status import raise_for_status
25
25
from ...providers.response import JsonConversation, ImageResponse, Sources, TitleGeneration, Reasoning, RequestLogin
26
26
from ...cookies import get_cookies
27
from .models import default_model, fallback_models, image_models, model_aliases, llama_models
27
from .models import default_model, default_vision_model, fallback_models, image_models, model_aliases
28
28
from ... import debug
29
29
30
30
class Conversation(JsonConversation):
@@ -41,6 +41,7 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
41
41
supports_stream = True
42
42
needs_auth = True
43
43
default_model = default_model
44
default_vision_model = default_vision_model
44
45
model_aliases = model_aliases
45
46
image_models = image_models
46
47
text_models = fallback_models
@@ -107,8 +108,8 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
107
108
) -> AsyncResult:
108
109
if not has_curl_cffi:
109
110
raise MissingRequirementsError('Install "curl_cffi" package | pip install -U curl_cffi')
110
if model == llama_models["name"]:
111
model = llama_models["text"] if media is None else llama_models["vision"]
111
if not model and media is not None:
112
model = cls.default_vision_model
112
113
model = cls.get_model(model)
113
114
114
115
session = Session(**auth_result.get_dict())
@@ -6,27 +6,30 @@ from ...providers.types import Messages
6
6
from ...typing import MediaListType
7
7
from ...requests import StreamSession, raise_for_status
8
8
from ...errors import ModelNotSupportedError
9
from ...providers.helper import get_last_user_message
10
9
from ...providers.response import ProviderInfo
11
10
from ..template.OpenaiTemplate import OpenaiTemplate
12
from .models import model_aliases, vision_models, default_vision_model, llama_models, text_models
11
from .models import model_aliases, vision_models, default_llama_model, default_vision_model, text_models
13
12
from ... import debug
14
13
15
14
class HuggingFaceAPI(OpenaiTemplate):
16
label = "HuggingFace (Inference API)"
15
label = "HuggingFace (Text Generation)"
17
16
parent = "HuggingFace"
18
17
url = "https://api-inference.huggingface.com"
19
18
api_base = "https://api-inference.huggingface.co/v1"
20
19
working = True
21
20
needs_auth = True
22
21
23
default_model = default_vision_model
22
default_model = default_llama_model
24
23
default_vision_model = default_vision_model
25
24
vision_models = vision_models
26
25
model_aliases = model_aliases
27
26
fallback_models = text_models + vision_models
28
27
29
provider_mapping: dict[str, dict] = {}
28
provider_mapping: dict[str, dict] = {
29
"google/gemma-3-27b-it": {
30
"hf-inference/models/google/gemma-3-27b-it": {
31
"task": "conversational",
32
"providerId": "google/gemma-3-27b-it"}}}
30
33
31
34
@classmethod
32
35
def get_model(cls, model: str, **kwargs) -> str:
@@ -47,7 +50,9 @@ class HuggingFaceAPI(OpenaiTemplate):
47
50
if [
48
51
provider
49
52
for provider in model.get("inferenceProviderMapping")
50
if provider.get("task") == "conversational"]]
53
if provider.get("status") == "live" and provider.get("task") == "conversational"
54
]
55
] + list(cls.provider_mapping.keys())
51
56
else:
52
57
cls.models = cls.fallback_models
53
58
return cls.models
@@ -78,11 +83,12 @@ class HuggingFaceAPI(OpenaiTemplate):
78
83
media: MediaListType = None,
79
84
**kwargs
80
85
):
81
if model == llama_models["name"]:
82
model = llama_models["text"] if media is None else llama_models["vision"]
83
if model in cls.model_aliases:
84
model = cls.model_aliases[model]
86
if not model and media is not None:
87
model = cls.default_vision_model
88
model = cls.get_model(model)
85
89
provider_mapping = await cls.get_mapping(model, api_key)
90
if not provider_mapping:
91
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__}")
86
92
for provider_key in provider_mapping:
87
93
api_path = provider_key if provider_key == "novita" else f"{provider_key}/v1"
88
94
api_base = f"https://router.huggingface.co/{api_path}"
@@ -10,6 +10,7 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_p
10
10
from ...errors import ModelNotSupportedError, ResponseError
11
11
from ...requests import StreamSession, raise_for_status
12
12
from ...providers.response import FinishReason, ImageResponse
13
from ...image.copy_images import save_response_media
13
14
from ..helper import format_image_prompt, get_last_user_message
14
15
from .models import default_model, default_image_model, model_aliases, text_models, image_models, vision_models
15
16
from ... import debug
@@ -176,12 +177,10 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin):
176
177
debug.log(f"Special token: {is_special}")
177
178
yield FinishReason("stop" if is_special else "length")
178
179
else:
179
if response.headers["content-type"].startswith("image/"):
180
base64_data = base64.b64encode(b"".join([chunk async for chunk in response.iter_content()]))
181
url = f"data:{response.headers['content-type']};base64,{base64_data.decode()}"
182
yield ImageResponse(url, inputs)
183
else:
184
yield (await response.json())[0]["generated_text"].strip()
180
async for chunk in save_response_media(response, prompt):
181
yield chunk
182
return
183
yield (await response.json())[0]["generated_text"].strip()
185
184
186
185
def format_prompt_mistral(messages: Messages, do_continue: bool = False) -> str:
187
186
system_messages = [message["content"] for message in messages if message["role"] == "system"]
@@ -0,0 +1,175 @@
1
from __future__ import annotations
2
3
import random
4
import requests
5
6
from ...providers.types import Messages
7
from ...requests import StreamSession, raise_for_status
8
from ...errors import ModelNotSupportedError
9
from ...providers.helper import format_image_prompt
10
from ...providers.base_provider import AsyncGeneratorProvider, ProviderModelMixin
11
from ...providers.response import ProviderInfo, ImageResponse, VideoResponse
12
from ...image.copy_images import save_response_media
13
from ... import debug
14
15
class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
16
label = "HuggingFace (Image / Video Generation)"
17
parent = "HuggingFace"
18
url = "https://huggingface.co"
19
working = True
20
needs_auth = True
21
22
tasks = ["text-to-image", "text-to-video"]
23
provider_mapping: dict[str, dict] = {}
24
task_mapping: dict[str, str] = {}
25
26
@classmethod
27
def get_models(cls, **kwargs) -> list[str]:
28
if not cls.models:
29
url = "https://huggingface.co/api/models?inference=warm&expand[]=inferenceProviderMapping"
30
response = requests.get(url)
31
if response.ok:
32
models = response.json()
33
cls.models = [
34
model["id"]
35
for model in models
36
if [
37
provider
38
for provider in model.get("inferenceProviderMapping")
39
if provider.get("status") == "live" and provider.get("task") in cls.tasks
40
]
41
]
42
cls.task_mapping = {
43
model["id"]: [
44
provider.get("task")
45
for provider in model.get("inferenceProviderMapping")
46
].pop()
47
for model in models
48
}
49
else:
50
cls.models = []
51
return cls.models
52
53
@classmethod
54
async def get_mapping(cls, model: str, api_key: str = None):
55
if model in cls.provider_mapping:
56
return cls.provider_mapping[model]
57
headers = {
58
'Content-Type': 'application/json',
59
}
60
if api_key is not None:
61
headers["Authorization"] = f"Bearer {api_key}"
62
async with StreamSession(
63
timeout=30,
64
headers=headers,
65
) as session:
66
async with session.get(f"https://huggingface.co/api/models/{model}?expand[]=inferenceProviderMapping") as response:
67
await raise_for_status(response)
68
model_data = await response.json()
69
cls.provider_mapping[model] = {key: value for key, value in model_data.get("inferenceProviderMapping").items() if value["status"] == "live"}
70
return cls.provider_mapping[model]
71
72
@classmethod
73
async def create_async_generator(
74
cls,
75
model: str,
76
messages: Messages,
77
api_key: str = None,
78
extra_data: dict = {},
79
prompt: str = None,
80
proxy: str = None,
81
timeout: int = 0,
82
**kwargs
83
):
84
provider_mapping = await cls.get_mapping(model, api_key)
85
headers = {
86
'Accept-Encoding': 'gzip, deflate',
87
'Content-Type': 'application/json',
88
}
89
new_mapping = {
90
"hf-free" if key == "hf-inference" else key: value for key, value in provider_mapping.items()
91
if key in ["replicate", "together", "hf-inference"]
92
}
93
provider_mapping = {**new_mapping, **provider_mapping}
94
last_response = None
95
for provider_key, provider in provider_mapping.items():
96
yield ProviderInfo(**{**cls.get_dict(), "label": f"HuggingFace ({provider_key})", "url": f"{cls.url}/{model}"})
97
98
api_base = f"https://router.huggingface.co/{provider_key}"
99
task = provider["task"]
100
provider_id = provider["providerId"]
101
if task not in cls.tasks:
102
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} task: {task}")
103
104
prompt = format_image_prompt(messages, prompt)
105
if task == "text-to-video":
106
extra_data = {
107
"num_inference_steps": 20,
108
"video_size": "landscape_16_9",
109
**extra_data
110
}
111
else:
112
extra_data = {
113
"width": 1024,
114
"height": 1024,
115
**extra_data
116
}
117
if provider_key == "fal-ai":
118
url = f"{api_base}/{provider_id}"
119
data = {
120
"prompt": prompt,
121
"image_size": "square_hd",
122
**extra_data
123
}
124
elif provider_key == "replicate":
125
url = f"{api_base}/v1/models/{provider_id}/prediction"
126
data = {
127
"input": {
128
"prompt": prompt,
129
**extra_data
130
}
131
}
132
elif provider_key in ("hf-inference", "hf-free"):
133
api_base = "https://api-inference.huggingface.co"
134
url = f"{api_base}/models/{provider_id}"
135
data = {
136
"inputs": prompt,
137
"parameters": {
138
"seed": random.randint(0, 2**32),
139
**extra_data
140
}
141
}
142
elif task == "text-to-image":
143
url = f"{api_base}/v1/images/generations"
144
data = {
145
"response_format": "url",
146
"prompt": prompt,
147
"model": provider_id,
148
**extra_data
149
}
150
151
async with StreamSession(
152
headers=headers if provider_key == "free" or api_key is None else {**headers, "Authorization": f"Bearer {api_key}"},
153
proxy=proxy,
154
timeout=timeout
155
) as session:
156
async with session.post(url, json=data) as response:
157
if response.status in (400, 401, 402):
158
last_response = response
159
debug.error(f"{cls.__name__}: Error {response.status} with {provider_key} and {provider_id}")
160
continue
161
if response.status == 404:
162
raise ModelNotSupportedError(f"Model is not supported: {model}")
163
await raise_for_status(response)
164
async for chunk in save_response_media(response, prompt):
165
yield chunk
166
return
167
result = await response.json()
168
if "video" in result:
169
yield VideoResponse(result["video"]["url"], prompt)
170
elif task == "text-to-image":
171
yield ImageResponse([item["url"] for item in result.get("images", result.get("data"))], prompt)
172
elif task == "text-to-video":
173
yield VideoResponse(result["output"], prompt)
174
return
175
await raise_for_status(last_response)
@@ -9,6 +9,7 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
9
9
from .HuggingChat import HuggingChat
10
10
from .HuggingFaceAPI import HuggingFaceAPI
11
11
from .HuggingFaceInference import HuggingFaceInference
12
from .HuggingFaceMedia import HuggingFaceMedia
12
13
from .models import model_aliases, vision_models, default_vision_model
13
14
from ... import debug
14
15
@@ -51,6 +52,12 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
51
52
debug.error(f"{cls.__name__} {type(e).__name__}; {e}")
52
53
if not cls.image_models:
53
54
cls.get_models()
55
try:
56
async for chunk in HuggingFaceMedia.create_async_generator(model, messages, **kwargs):
57
yield chunk
58
return
59
except ModelNotSupportedError:
60
pass
54
61
if model in cls.image_models:
55
62
if "api_key" not in kwargs:
56
63
async for chunk in HuggingChat.create_async_generator(model, messages, **kwargs):
@@ -47,9 +47,5 @@ extra_models = [
47
47
"NousResearch/Hermes-3-Llama-3.1-8B",
48
48
]
49
49
default_vision_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
50
vision_models = [default_vision_model, "Qwen/Qwen2-VL-7B-Instruct"]
51
llama_models = {
52
"name": "llama-3",
53
"text": "meta-llama/Llama-3.3-70B-Instruct",
54
"vision": "meta-llama/Llama-3.2-11B-Vision-Instruct",
55
}
50
default_llama_model = "meta-llama/Llama-3.3-70B-Instruct"
51
vision_models = [default_vision_model, "Qwen/Qwen2-VL-7B-Instruct"]
@@ -67,7 +67,6 @@ class BlackForestLabs_Flux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
67
67
zerogpu_uuid: str = "[object Object]",
68
68
**kwargs
69
69
) -> AsyncResult:
70
model = cls.get_model(model)
71
70
async with StreamSession(impersonate="chrome", proxy=proxy) as session:
72
71
prompt = format_image_prompt(messages, prompt)
73
72
data = [prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
@@ -37,8 +37,6 @@ class BlackForestLabs_Flux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
37
37
randomize_seed: bool = True,
38
38
**kwargs
39
39
) -> AsyncResult:
40
41
model = cls.get_model(model)
42
40
width = max(32, width - (width % 8))
43
41
height = max(32, height - (height % 8))
44
42
prompt = format_image_prompt(messages, prompt)
@@ -24,9 +24,14 @@ class CohereForAI_C4AI_Command(AsyncGeneratorProvider, ProviderModelMixin):
24
24
"command-r": "command-r",
25
25
"command-r7b": "command-r7b-12-2024",
26
26
}
27
28
27
models = list(model_aliases.keys())
29
28
29
@classmethod
30
def get_model(cls, model: str, **kwargs) -> str:
31
if model in cls.model_aliases.values():
32
return model
33
return super().get_model(model, **kwargs)
34
30
35
@classmethod
31
36
async def create_async_generator(
32
37
cls, model: str, messages: Messages,
@@ -203,7 +203,21 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
203
203
Returns:
204
204
A list of messages with the user input and the image, if any
205
205
"""
206
# Create a message object with the user role and the content
206
# merged_messages = []
207
# last_message = None
208
# for message in messages:
209
# current_message = last_message
210
# if current_message is not None:
211
# if current_message["role"] == message["role"]:
212
# current_message["content"] += "\n" + message["content"]
213
# else:
214
# merged_messages.append(current_message)
215
# last_message = message.copy()
216
# else:
217
# last_message = message.copy()
218
# if last_message is not None:
219
# merged_messages.append(last_message)
220
207
221
messages = [{
208
222
"id": str(uuid.uuid4()),
209
223
"author": {"role": message["role"]},