返回提交历史
Added
docs/media.md
+244
-0
Modified
g4f/Provider/Cloudflare.py
+14
-20
Modified
g4f/Provider/PerplexityLabs.py
+1
-1
Modified
g4f/Provider/PollinationsAI.py
+1
-1
Modified
g4f/Provider/TypeGPT.py
+25
-2
Modified
g4f/Provider/hf/HuggingFaceAPI.py
+11
-6
Modified
g4f/Provider/hf/HuggingFaceMedia.py
+29
-21
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+1
-0
Modified
g4f/client/__init__.py
+12
-6
Modified
g4f/errors.py
+3
-0
Modified
g4f/gui/client/background.html
+3
-3
Modified
g4f/gui/client/index.html
+5
-0
Modified
g4f/gui/client/static/css/style.css
+2
-10
Modified
g4f/gui/client/static/js/chat.v1.js
+49
-25
Modified
g4f/gui/client/static/js/photoswipe.js
+1
-0
Modified
g4f/image/__init__.py
+3
-0
Modified
g4f/image/copy_images.py
+5
-5
Modified
g4f/providers/base_provider.py
+5
-1
Modified
g4f/providers/response.py
+6
-5
Modified
g4f/requests/__init__.py
+1
-0
Modified
g4f/tools/files.py
+2
-2
Modified
g4f/tools/media.py
+2
-2
XFEstudio/gpt4free
Delete buckets with the conversation Fix lightbox for uploaded images Fix save media content Fix TypeGPT and Cloudflare provider
fa17ce1b
代码差异
22 个文件
+425
-110
@@ -0,0 +1,244 @@
1
### G4F - Media Documentation
2
3
This document outlines how to use the G4F (Generative Framework) library to generate and process various media types, including audio, images, and videos.
4
5
---
6
7
### 1. **Audio Generation and Transcription**
8
9
G4F supports audio generation through providers like PollinationsAI and audio transcription using providers like Microsoft_Phi_4.
10
11
#### **Generate Audio with PollinationsAI:**
12
13
```python
14
import asyncio
15
from g4f.client import AsyncClient
16
import g4f.Provider
17
18
async def main():
19
client = AsyncClient(provider=g4f.Provider.PollinationsAI)
20
21
response = await client.chat.completions.create(
22
model="openai-audio",
23
messages=[{"role": "user", "content": "Say good day to the world"}],
24
audio={"voice": "alloy", "format": "mp3"},
25
)
26
response.choices[0].message.save("alloy.mp3")
27
28
asyncio.run(main())
29
```
30
31
#### **Transcribe an Audio File:**
32
33
```python
34
import asyncio
35
from g4f.client import AsyncClient
36
import g4f.Provider
37
38
async def main():
39
client = AsyncClient(provider=g4f.Provider.Microsoft_Phi_4)
40
41
with open("audio.wav", "rb") as audio_file:
42
response = await client.chat.completions.create(
43
messages="Transcribe this audio",
44
provider=g4f.Provider.Microsoft_Phi_4,
45
media=[[audio_file, "audio.wav"]],
46
modalities=["text"],
47
)
48
print(response.choices[0].message.content)
49
50
asyncio.run(main())
51
```
52
53
---
54
55
### 2. **Image Generation**
56
57
G4F can generate images from text prompts and provides options to retrieve images as URLs or base64-encoded strings.
58
59
#### **Generate an Image:**
60
61
```python
62
import asyncio
63
from g4f.client import AsyncClient
64
65
async def main():
66
client = AsyncClient()
67
68
response = await client.images.generate(
69
prompt="a white siamese cat",
70
model="flux",
71
response_format="url",
72
)
73
74
image_url = response.data[0].url
75
print(f"Generated image URL: {image_url}")
76
77
asyncio.run(main())
78
```
79
80
#### **Base64 Response Format:**
81
82
```python
83
import asyncio
84
from g4f.client import AsyncClient
85
86
async def main():
87
client = AsyncClient()
88
89
response = await client.images.generate(
90
prompt="a white siamese cat",
91
model="flux",
92
response_format="b64_json",
93
)
94
95
base64_text = response.data[0].b64_json
96
print(base64_text)
97
98
asyncio.run(main())
99
```
100
101
#### **Image Parameters:**
102
- **`width`**: Defines the width of the generated image.
103
- **`height`**: Defines the height of the generated image.
104
- **`n`**: Specifies the number of images to generate.
105
- **`response_format`**: Specifies the format of the response:
106
- `"url"`: Returns the URL of the image.
107
- `"b64_json"`: Returns the image as a base64-encoded JSON string.
108
- (Default): Saves the image locally and returns a local url.
109
110
#### **Example with Image Parameters:**
111
112
```python
113
import asyncio
114
from g4f.client import AsyncClient
115
116
async def main():
117
client = AsyncClient()
118
119
response = await client.images.generate(
120
prompt="a white siamese cat",
121
model="flux",
122
response_format="url",
123
width=512,
124
height=512,
125
n=2,
126
)
127
128
for image in response.data:
129
print(f"Generated image URL: {image.url}")
130
131
asyncio.run(main())
132
```
133
134
---
135
136
### 3. **Creating Image Variations**
137
138
You can generate variations of an existing image using G4F.
139
140
#### **Create Image Variations:**
141
142
```python
143
import asyncio
144
from g4f.client import AsyncClient
145
from g4f.Provider import OpenaiChat
146
147
async def main():
148
client = AsyncClient(image_provider=OpenaiChat)
149
150
response = await client.images.create_variation(
151
prompt="a white siamese cat",
152
image=open("docs/images/cat.jpg", "rb"),
153
model="dall-e-3",
154
)
155
156
image_url = response.data[0].url
157
print(f"Generated image URL: {image_url}")
158
159
asyncio.run(main())
160
```
161
162
---
163
164
### 4. **Video Generation**
165
166
G4F supports video generation through providers like HuggingFaceMedia.
167
168
#### **Generate a Video:**
169
170
```python
171
import asyncio
172
from g4f.client import AsyncClient
173
from g4f.Provider import HuggingFaceMedia
174
175
async def main():
176
client = AsyncClient(
177
provider=HuggingFaceMedia,
178
api_key="hf_***" # Your API key here
179
)
180
181
video_models = client.models.get_video()
182
print("Available Video Models:", video_models)
183
184
result = await client.media.generate(
185
model=video_models[0],
186
prompt="G4F AI technology is the best in the world.",
187
response_format="url",
188
)
189
190
print("Generated Video URL:", result.data[0].url)
191
192
asyncio.run(main())
193
```
194
195
#### **Video Parameters:**
196
- **`resolution`**: Specifies the resolution of the generated video. Options include:
197
- `"480p"` (default)
198
- `"720p"`
199
- **`aspect_ratio`**: Defines the width-to-height ratio (e.g., `"16:9"`).
200
- **`n`**: Specifies the number of videos to generate.
201
- **`response_format`**: Specifies the format of the response:
202
- `"url"`: Returns the URL of the video.
203
- `"b64_json"`: Returns the video as a base64-encoded JSON string.
204
- (Default): Saves the video locally and returns a local url.
205
206
#### **Example with Video Parameters:**
207
208
```python
209
import os
210
import asyncio
211
from g4f.client import AsyncClient
212
from g4f.Provider import HuggingFaceMedia
213
214
async def main():
215
client = AsyncClient(
216
provider=HuggingFaceMedia,
217
api_key=os.getenv("HUGGINGFACE_API_KEY") # Your API key here
218
)
219
220
video_models = client.models.get_video()
221
print("Available Video Models:", video_models)
222
223
result = await client.media.generate(
224
model=video_models[0],
225
prompt="G4F AI technology is the best in the world.",
226
resolution="720p",
227
aspect_ratio="16:9",
228
n=1,
229
response_format="url",
230
)
231
232
print("Generated Video URL:", result.data[0].url)
233
234
asyncio.run(main())
235
```
236
237
---
238
239
**Key Points:**
240
241
- **Provider Selection**: Ensure the selected provider supports the desired media generation or processing task.
242
- **API Keys**: Some providers require API keys for authentication.
243
- **Response Formats**: Use `response_format` to control the output format (URL, base64, local file).
244
- **Parameter Usage**: Use parameters like `width`, `height`, `resolution`, `aspect_ratio`, and `n` to customize the generated media.
@@ -2,14 +2,12 @@ from __future__ import annotations
2
2
3
3
import asyncio
4
4
import json
5
from pathlib import Path
6
5
7
6
from ..typing import AsyncResult, Messages, Cookies
8
7
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin, get_running_loop
9
8
from ..requests import Session, StreamSession, get_args_from_nodriver, raise_for_status, merge_cookies
10
9
from ..requests import DEFAULT_HEADERS, has_nodriver, has_curl_cffi
11
from ..providers.response import FinishReason
12
from ..cookies import get_cookies_dir
10
from ..providers.response import FinishReason, Usage
13
11
from ..errors import ResponseStatusError, ModelNotFoundError
14
12
15
13
class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
@@ -84,11 +82,16 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
84
82
except ModelNotFoundError:
85
83
pass
86
84
data = {
87
"messages": messages,
85
"messages": [{
86
"role":"user",
87
"content": message["content"] if isinstance(message["content"], str) else "",
88
"parts": [{"type":"text", "text":message["content"]}] if isinstance(message["content"], str) else message["content"]} for message in messages],
88
89
"lora": None,
89
90
"model": model,
90
91
"max_tokens": max_tokens,
91
"stream": True
92
"stream": True,
93
"system_message":"You are a helpful assistant",
94
"tools":[]
92
95
}
93
96
async with StreamSession(**cls._args) as session:
94
97
async with session.post(
@@ -103,22 +106,13 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
103
106
if cache_file.exists():
104
107
cache_file.unlink()
105
108
raise
106
reason = None
107
109
async for line in response.iter_lines():
108
if line.startswith(b'data: '):
109
if line == b'data: [DONE]':
110
break
111
try:
112
content = json.loads(line[6:].decode())
113
if content.get("response") and content.get("response") != '</s>':
114
yield content['response']
115
reason = "max_tokens"
116
elif content.get("response") == '':
117
reason = "stop"
118
except Exception:
119
continue
120
if reason is not None:
121
yield FinishReason(reason)
110
if line.startswith(b'0:'):
111
yield json.loads(line[2:])
112
elif line.startswith(b'e:'):
113
finish = json.loads(line[2:])
114
yield Usage(**finish.get("usage"))
115
yield FinishReason(finish.get("finishReason"))
122
116
123
117
with cache_file.open("w") as f:
124
118
json.dump(cls._args, f)
@@ -68,7 +68,7 @@ class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
68
68
"version": "2.18",
69
69
"source": "default",
70
70
"model": model,
71
"messages": messages,
71
"messages": [message for message in messages if isinstance(message["content"], str)],
72
72
}
73
73
await ws.send_str("42" + json.dumps(["perplexity_labs", message_data]))
74
74
last_message = 0
@@ -155,7 +155,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
155
155
media: MediaListType = None,
156
156
temperature: float = None,
157
157
presence_penalty: float = None,
158
top_p: float = 1,
158
top_p: float = None,
159
159
frequency_penalty: float = None,
160
160
response_format: Optional[dict] = None,
161
161
extra_parameters: list[str] = ["tools", "parallel_tool_calls", "tool_choice", "reasoning_effort", "logit_bias", "voice", "modalities", "audio"],
@@ -1,18 +1,41 @@
1
1
from __future__ import annotations
2
2
3
import requests
4
3
5
from .template import OpenaiTemplate
4
6
5
7
class TypeGPT(OpenaiTemplate):
6
8
label = "TypeGpt"
7
9
url = "https://chat.typegpt.net"
8
api_base = "https://chat.typegpt.net/api/openai/typegpt/v1"
10
api_base = "https://chat.typegpt.net/api/openai/v1"
9
11
working = True
12
headers = {
13
"accept": "application/json, text/event-stream",
14
"accept-language": "de,en-US;q=0.9,en;q=0.8",
15
"content-type": "application/json",
16
"priority": "u=1, i",
17
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
18
"sec-ch-ua-mobile": "?0",
19
"sec-ch-ua-platform": "\"Linux\"",
20
"sec-fetch-dest": "empty",
21
"sec-fetch-mode": "cors",
22
"sec-fetch-site": "same-origin",
23
"referer": "https://chat.typegpt.net/",
24
}
10
25
11
26
default_model = 'gpt-4o-mini-2024-07-18'
12
27
default_vision_model = default_model
13
28
vision_models = ['gpt-3.5-turbo', 'gpt-3.5-turbo-202201', default_vision_model, "o3-mini"]
14
models = vision_models + ["deepseek-r1", "deepseek-v3", "evil", "o1"]
29
fallback_models = vision_models + ["deepseek-r1", "deepseek-v3", "evil", "o1"]
30
image_models = ["Image-Generator"]
15
31
model_aliases = {
16
32
"gpt-3.5-turbo": "gpt-3.5-turbo-202201",
17
33
"gpt-4o-mini": "gpt-4o-mini-2024-07-18",
18
34
}
35
36
@classmethod
37
def get_models(cls, **kwargs):
38
if not cls.models:
39
cls.models = requests.get(f"{cls.url}/api/config").json()["customModels"].split(",")
40
cls.models = [model.split("@")[0][1:] for model in cls.models if model.startswith("+") and model not in cls.image_models]
41
return cls.models
@@ -5,11 +5,10 @@ import requests
5
5
from ...providers.types import Messages
6
6
from ...typing import MediaListType
7
7
from ...requests import StreamSession, raise_for_status
8
from ...errors import ModelNotSupportedError
8
from ...errors import ModelNotSupportedError, PaymentRequiredError
9
9
from ...providers.response import ProviderInfo
10
10
from ..template.OpenaiTemplate import OpenaiTemplate
11
11
from .models import model_aliases, vision_models, default_llama_model, default_vision_model, text_models
12
from ... import debug
13
12
14
13
class HuggingFaceAPI(OpenaiTemplate):
15
14
label = "HuggingFace (Text Generation)"
@@ -89,6 +88,7 @@ class HuggingFaceAPI(OpenaiTemplate):
89
88
provider_mapping = await cls.get_mapping(model, api_key)
90
89
if not provider_mapping:
91
90
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__}")
91
error = None
92
92
for provider_key in provider_mapping:
93
93
api_path = provider_key if provider_key == "novita" else f"{provider_key}/v1"
94
94
api_base = f"https://router.huggingface.co/{api_path}"
@@ -97,7 +97,6 @@ class HuggingFaceAPI(OpenaiTemplate):
97
97
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} task: {task}")
98
98
model = provider_mapping[provider_key]["providerId"]
99
99
yield ProviderInfo(**{**cls.get_dict(), "label": f"HuggingFace ({provider_key})"})
100
break
101
100
# start = calculate_lenght(messages)
102
101
# if start > max_inputs_lenght:
103
102
# if len(messages) > 6:
@@ -109,8 +108,14 @@ class HuggingFaceAPI(OpenaiTemplate):
109
108
# if len(messages) > 1 and calculate_lenght(messages) > max_inputs_lenght:
110
109
# messages = last_user_message
111
110
# debug.log(f"Messages trimmed from: {start} to: {calculate_lenght(messages)}")
112
async for chunk in super().create_async_generator(model, messages, api_base=api_base, api_key=api_key, max_tokens=max_tokens, media=media, **kwargs):
113
yield chunk
114
111
try:
112
async for chunk in super().create_async_generator(model, messages, api_base=api_base, api_key=api_key, max_tokens=max_tokens, media=media, **kwargs):
113
yield chunk
114
return
115
except PaymentRequiredError as e:
116
error = e
117
continue
118
if error is not None:
119
raise error
115
120
def calculate_lenght(messages: Messages) -> int:
116
121
return sum([len(message["content"]) + 16 for message in messages])
@@ -101,7 +101,14 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
101
101
prompt: str = None,
102
102
proxy: str = None,
103
103
timeout: int = 0,
104
# Video & Image Generation
105
n: int = 1,
104
106
aspect_ratio: str = None,
107
# Only for Image Generation
108
height: int = None,
109
width: int = None,
110
# Video Generation
111
resolution: str = "480p",
105
112
**kwargs
106
113
):
107
114
selected_provider = None
@@ -109,6 +116,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
109
116
model, selected_provider = model.split(":", 1)
110
117
elif not model:
111
118
model = cls.get_models()[0]
119
prompt = format_image_prompt(messages, prompt)
112
120
provider_mapping = await cls.get_mapping(model, api_key)
113
121
headers = {
114
122
'Accept-Encoding': 'gzip, deflate',
@@ -119,7 +127,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
119
127
if key in ["replicate", "together", "hf-inference"]
120
128
}
121
129
provider_mapping = {**new_mapping, **provider_mapping}
122
async def generate(extra_data: dict, prompt: str, aspect_ratio: str = None):
130
async def generate(extra_data: dict, aspect_ratio: str = None):
123
131
last_response = None
124
132
for provider_key, provider in provider_mapping.items():
125
133
if selected_provider is not None and selected_provider != provider_key:
@@ -132,34 +140,29 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
132
140
if task not in cls.tasks:
133
141
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} task: {task}")
134
142
135
prompt = format_image_prompt(messages, prompt)
136
143
if aspect_ratio is None:
137
144
aspect_ratio = "1:1" if task == "text-to-image" else "16:9"
138
145
if task == "text-to-video" and provider_key != "novita":
139
146
extra_data = {
140
147
"num_inference_steps": 20,
141
"resolution": "480p",
148
"resolution": resolution,
142
149
"aspect_ratio": aspect_ratio,
143
150
**extra_data
144
151
}
145
152
else:
146
extra_data = use_aspect_ratio(extra_data, aspect_ratio)
153
extra_data = use_aspect_ratio({
154
**extra_data,
155
"height": height,
156
"width": width,
157
}, aspect_ratio)
147
158
url = f"{api_base}/{provider_id}"
148
159
data = {
149
160
"prompt": prompt,
150
161
**extra_data
151
162
}
152
163
if provider_key == "fal-ai" and task == "text-to-image":
153
if aspect_ratio is None or aspect_ratio == "1:1":
154
image_size = "square_hd",
155
elif aspect_ratio == "16:9":
156
image_size = "landscape_hd",
157
elif aspect_ratio == "9:16":
158
image_size = "portrait_16_9"
159
else:
160
image_size = extra_data # width, height
161
164
data = {
162
"image_size": image_size,
165
"image_size": extra_data,
163
166
**data
164
167
}
165
168
elif provider_key == "novita":
@@ -212,16 +215,21 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
212
215
await raise_for_status(last_response)
213
216
214
217
background_tasks = set()
218
running_tasks = set()
215
219
started = time.time()
216
task = asyncio.create_task(generate(extra_data, prompt, aspect_ratio))
217
background_tasks.add(task)
218
task.add_done_callback(background_tasks.discard)
219
while background_tasks:
220
while n > 0:
221
n -= 1
222
task = asyncio.create_task(generate(extra_data, aspect_ratio))
223
background_tasks.add(task)
224
running_tasks.add(task)
225
task.add_done_callback(running_tasks.discard)
226
while running_tasks:
220
227
diff = time.time() - started
221
228
if diff > 1:
222
229
yield Reasoning(label="Generating", status=f"{diff:.2f}s")
223
230
await asyncio.sleep(0.2)
224
provider_info, media_response = await task
225
yield Reasoning(label="Finished", status=f"{time.time() - started:.2f}s")
226
yield provider_info
227
yield media_response
231
for task in background_tasks:
232
provider_info, media_response = await task
233
yield Reasoning(label="Finished", status=f"{time.time() - started:.2f}s")
234
yield provider_info
235
yield media_response
@@ -142,6 +142,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
142
142
An ImageRequest object that contains the download URL, file name, and other data
143
143
"""
144
144
async def upload_image(image, image_name):
145
debug.log(f"Uploading image: {image_name}")
145
146
# Convert the image to a PIL Image object and get the extension
146
147
data_bytes = to_bytes(image)
147
148
image = to_image(data_bytes)
@@ -429,7 +429,7 @@ class Images:
429
429
**kwargs
430
430
) -> MediaResponse:
431
431
messages = [{"role": "user", "content": f"{prompt_prefix}{prompt}"}]
432
response = None
432
items: list[MediaResponse] = []
433
433
if hasattr(provider_handler, "create_async_generator"):
434
434
async for item in provider_handler.create_async_generator(
435
435
model,
@@ -439,8 +439,7 @@ class Images:
439
439
**kwargs
440
440
):
441
441
if isinstance(item, MediaResponse):
442
response = item
443
break
442
items.append(item)
444
443
elif hasattr(provider_handler, "create_completion"):
445
444
for item in provider_handler.create_completion(
446
445
model,
@@ -450,11 +449,18 @@ class Images:
450
449
**kwargs
451
450
):
452
451
if isinstance(item, MediaResponse):
453
response = item
454
break
452
items.append(item)
455
453
else:
456
454
raise ValueError(f"Provider {provider_name} does not support image generation")
457
return response
455
urls = []
456
for item in items:
457
if isinstance(item.urls, str):
458
urls.append(item.urls)
459
elif isinstance(item.urls, list):
460
urls.extend(item.urls)
461
if not urls:
462
return None
463
return MediaResponse(urls, items[0].alt)
458
464
459
465
def create_variation(
460
466
self,
@@ -34,6 +34,9 @@ class NestAsyncioError(MissingRequirementsError):
34
34
class MissingAuthError(Exception):
35
35
...
36
36
37
class PaymentRequiredError(Exception):
38
...
39
37
40
class NoMediaResponseError(Exception):
38
41
...
39
42
@@ -174,8 +174,8 @@
174
174
skipImage++;
175
175
return;
176
176
}
177
if (skipRefresh) {
178
skipRefresh = 0;
177
if (skipRefresh > 0) {
178
skipRefresh -= 1;
179
179
return;
180
180
}
181
181
if (images.length > 0) {
@@ -197,7 +197,7 @@
197
197
};
198
198
imageFeed.onclick = () => {
199
199
imageFeed.src = "/search/image?random=" + Math.random();
200
skipRefresh = 1;
200
skipRefresh = 2;
201
201
};
202
202
})();
203
203
</script>
@@ -150,6 +150,11 @@
150
150
<input type="checkbox" id="countTokens" checked/>
151
151
<label for="countTokens" class="toogle" title=""></label>
152
152
</div>
153
<div class="field">
154
<span class="label">Automatic Orientation (16:9 or 9:16)</span>
155
<input type="checkbox" id="automaticOrientation" checked/>
156
<label for="automaticOrientation" class="toogle" title=""></label>
157
</div>
153
158
<div class="field box">
154
159
<label for="systemPrompt" class="label">System prompt</label>
155
160
<textarea id="systemPrompt" placeholder="You are a helpful assistant." data-example="If you need to generate images, you can use the following format: . This will enable the use of an image generation tool."></textarea>