返回提交历史
Modified
g4f/Provider/hf/HuggingFaceMedia.py
+7
-5
Modified
g4f/gui/client/qrcode.html
+1
-10
Modified
g4f/gui/client/static/js/chat.v1.js
+25
-37
Modified
g4f/image/__init__.py
+4
-4
Modified
g4f/providers/base_provider.py
+1
-1
Modified
g4f/providers/helper.py
+11
-2
Modified
g4f/requests/raise_for_status.py
+2
-2
Modified
g4f/tools/media.py
+11
-4
XFEstudio/gpt4free
Insert buckets as content part Fix copy button for code results Use crypto for uuids in UI
7db18c2a
代码差异
8 个文件
+62
-65
@@ -119,7 +119,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
119
119
if key in ["replicate", "together", "hf-inference"]
120
120
}
121
121
provider_mapping = {**new_mapping, **provider_mapping}
122
async def generate(extra_data: dict, prompt: str):
122
async def generate(extra_data: dict, prompt: str, aspect_ratio: str = None):
123
123
last_response = None
124
124
for provider_key, provider in provider_mapping.items():
125
125
if selected_provider is not None and selected_provider != provider_key:
@@ -133,15 +133,17 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
133
133
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} task: {task}")
134
134
135
135
prompt = format_image_prompt(messages, prompt)
136
if task == "text-to-video":
136
if aspect_ratio is None:
137
aspect_ratio = "1:1" if task == "text-to-image" else "16:9"
138
if task == "text-to-video" and provider_key != "novita":
137
139
extra_data = {
138
140
"num_inference_steps": 20,
139
141
"resolution": "480p",
140
"aspect_ratio": "16:9" if aspect_ratio is None else aspect_ratio,
142
"aspect_ratio": aspect_ratio,
141
143
**extra_data
142
144
}
143
145
else:
144
extra_data = use_aspect_ratio(extra_data, "1:1" if aspect_ratio is None else aspect_ratio)
146
extra_data = use_aspect_ratio(extra_data, aspect_ratio)
145
147
url = f"{api_base}/{provider_id}"
146
148
data = {
147
149
"prompt": prompt,
@@ -211,7 +213,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
211
213
212
214
background_tasks = set()
213
215
started = time.time()
214
task = asyncio.create_task(generate(extra_data, prompt))
216
task = asyncio.create_task(generate(extra_data, prompt, aspect_ratio))
215
217
background_tasks.add(task)
216
218
task.add_done_callback(background_tasks.discard)
217
219
while background_tasks:
@@ -39,15 +39,6 @@
39
39
}
40
40
41
41
import QrScanner from 'https://cdn.jsdelivr.net/npm/qr-scanner/qr-scanner.min.js';
42
43
function generate_uuid() {
44
function random16Hex() { return (0x10000 | Math.random() * 0x10000).toString(16).substr(1); }
45
return random16Hex() + random16Hex() +
46
"-" + random16Hex() +
47
"-" + random16Hex() +
48
"-" + random16Hex() +
49
"-" + random16Hex() + random16Hex() + random16Hex();
50
}
51
42
52
43
const videoElem = document.getElementById('video');
53
44
const camStatus = document.getElementById('cam-status');
@@ -80,7 +71,7 @@
80
71
method: 'DELETE'
81
72
});
82
73
}
83
share_id = generate_uuid();
74
share_id = crypto.randomUUID();
84
75
85
76
const url = `${share_url}/backend-api/v2/chat/${encodeURI(share_id)}`;
86
77
const response = await fetch(url, {
@@ -69,6 +69,10 @@ if (window.markdownit) {
69
69
markdown_render = (content) => {
70
70
if (Array.isArray(content)) {
71
71
content = content.map((item) => {
72
if (!item.name) {
73
size = parseInt(appStorage.getItem(`bucket:${item.bucket_id}`), 10);
74
return `**Bucket:** [[${item.bucket_id}]](${item.url})${size ? ` (${formatFileSize(size)})` : ""}`
75
}
72
76
if (item.name.endsWith(".wav") || item.name.endsWith(".mp3")) {
73
77
return `<audio controls src="${item.url}"></audio>`;
74
78
}
@@ -78,14 +82,8 @@ if (window.markdownit) {
78
82
return `[]()`;
79
83
}).join("\n");
80
84
}
81
return markdown.render(content
82
.replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
83
.replaceAll(/<img data-prompt="[^>]+">/gm, "")
84
.replaceAll(/{"bucket_id":"([^"]+)"}/gm, (match, p1) => {
85
size = parseInt(appStorage.getItem(`bucket:${p1}`), 10);
86
return `**Bucket:** [[${p1}]](/backend-api/v2/files/${p1})${size ? ` (${formatFileSize(size)})` : ""}`;
87
})
88
)
85
content = content.replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
86
return markdown.render(content)
89
87
.replaceAll("<a href=", '<a target="_blank" href=')
90
88
.replaceAll('<code>', '<code class="language-plaintext">')
91
89
.replaceAll('<i class="', '<i class="')
@@ -113,7 +111,7 @@ function render_reasoning(reasoning, final = false) {
113
111
}
114
112
115
113
function render_reasoning_text(reasoning) {
116
return `Reasoning 🧠: ${reasoning.status}\n\n${reasoning.text}\n\n`;
114
return `${reasoning.label ? reasoning.label :'Reasoning 🧠'}: ${reasoning.status}\n\n${reasoning.text}\n\n`;
117
115
}
118
116
119
117
function filter_message(text) {
@@ -168,7 +166,10 @@ const iframe_close = Object.assign(document.createElement("button"), {
168
166
className: "hljs-iframe-close",
169
167
innerHTML: '<i class="fa-regular fa-x"></i>',
170
168
});
171
iframe_close.onclick = () => iframe_container.classList.add("hidden");
169
iframe_close.onclick = () => {
170
iframe_container.classList.add("hidden");
171
iframe.src = "";
172
}
172
173
iframe_container.appendChild(iframe_close);
173
174
document.body.appendChild(iframe_container);
174
175
@@ -200,10 +201,6 @@ class HtmlRenderPlugin {
200
201
}
201
202
}
202
203
}
203
if (window.hljs) {
204
hljs.addPlugin(new HtmlRenderPlugin())
205
hljs.addPlugin(new CopyButtonPlugin());
206
}
207
204
let typesetPromise = Promise.resolve();
208
205
const highlight = (container) => {
209
206
if (window.hljs) {
@@ -515,7 +512,7 @@ const handle_ask = async (do_ask_gpt = true, message = null) => {
515
512
const file = new File([blob], 'downloads.json', { type: 'application/json' }); // Create File object
516
513
let formData = new FormData();
517
514
formData.append('files', file); // Append as a file
518
const bucket_id = uuid();
515
const bucket_id = crypto.randomUUID();
519
516
await fetch(`/backend-api/v2/files/${bucket_id}`, {
520
517
method: 'POST',
521
518
body: formData
@@ -1339,7 +1336,7 @@ const set_conversation = async (conversation_id) => {
1339
1336
1340
1337
const new_conversation = async () => {
1341
1338
history.pushState({}, null, `/chat/`);
1342
window.conversation_id = uuid();
1339
window.conversation_id = crypto.randomUUID();
1343
1340
document.title = window.title || document.title;
1344
1341
document.querySelector(".chat-header").innerText = "New Conversation - G4F";
1345
1342
@@ -1814,17 +1811,6 @@ hide_input.addEventListener("click", async (e) => {
1814
1811
document.querySelector(".chat-footer .buttons").classList[func]("hidden");
1815
1812
});
1816
1813
1817
const uuid = () => {
1818
return `xxxxxxxx-xxxx-4xxx-yxxx-${Date.now().toString(16)}`.replace(
1819
/[xy]/g,
1820
function (c) {
1821
var r = (Math.random() * 16) | 0,
1822
v = c == "x" ? r : (r & 0x3) | 0x8;
1823
return v.toString(16);
1824
}
1825
);
1826
};
1827
1828
1814
function generateSecureRandomString(length = 128) {
1829
1815
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
1830
1816
const array = new Uint8Array(length);
@@ -2171,7 +2157,7 @@ window.addEventListener('load', async function() {
2171
2157
window.addEventListener('DOMContentLoaded', async function() {
2172
2158
await on_load();
2173
2159
if (window.conversation_id == "{{conversation_id}}") {
2174
window.conversation_id = uuid();
2160
window.conversation_id = crypto.randomUUID();
2175
2161
} else {
2176
2162
await on_api();
2177
2163
}
@@ -2202,6 +2188,10 @@ async function on_load() {
2202
2188
//load_conversation(window.conversation_id);
2203
2189
}
2204
2190
load_conversations();
2191
if (window.hljs) {
2192
hljs.addPlugin(new HtmlRenderPlugin())
2193
hljs.addPlugin(new CopyButtonPlugin());
2194
}
2205
2195
}
2206
2196
2207
2197
const load_provider_option = (input, provider_name) => {
@@ -2599,7 +2589,7 @@ function formatFileSize(bytes) {
2599
2589
2600
2590
function connectToSSE(url, do_refine, bucket_id) {
2601
2591
const eventSource = new EventSource(url);
2602
eventSource.onmessage = (event) => {
2592
eventSource.onmessage = async (event) => {
2603
2593
const data = JSON.parse(event.data);
2604
2594
if (data.error) {
2605
2595
inputCount.innerText = `Error: ${data.error.message}`;
@@ -2624,12 +2614,10 @@ function connectToSSE(url, do_refine, bucket_id) {
2624
2614
}
2625
2615
appStorage.setItem(`bucket:${bucket_id}`, data.size);
2626
2616
inputCount.innerText = "Files are loaded successfully";
2627
if (!userInput.value) {
2628
userInput.value = JSON.stringify({bucket_id: bucket_id});
2629
handle_ask(false);
2630
} else {
2631
userInput.value += (userInput.value ? "\n" : "") + JSON.stringify({bucket_id: bucket_id}) + "\n";
2632
}
2617
2618
const url = `/backend-api/v2/files/${bucket_id}`;
2619
const media = [{bucket_id: bucket_id, url: url}];
2620
await handle_ask(false, media);
2633
2621
}
2634
2622
};
2635
2623
eventSource.onerror = (event) => {
@@ -2639,7 +2627,7 @@ function connectToSSE(url, do_refine, bucket_id) {
2639
2627
}
2640
2628
2641
2629
async function upload_files(fileInput) {
2642
const bucket_id = uuid();
2630
const bucket_id = crypto.randomUUID();
2643
2631
paperclip.classList.add("blink");
2644
2632
2645
2633
const formData = new FormData();
@@ -3016,7 +3004,7 @@ function import_memory() {
3016
3004
let count = 0;
3017
3005
let user_id = appStorage.getItem("user") || appStorage.getItem("mem0-user_id");
3018
3006
if (!user_id) {
3019
user_id = uuid();
3007
user_id = crypto.randomUUID();
3020
3008
appStorage.setItem("mem0-user_id", user_id);
3021
3009
}
3022
3010
inputCount.innerText = `Start importing to Mem0...`;
@@ -288,14 +288,14 @@ def use_aspect_ratio(extra_data: dict, aspect_ratio: str) -> Image:
288
288
}
289
289
elif aspect_ratio == "16:9":
290
290
extra_data = {
291
"width": 800,
292
"height": 512,
291
"width": 832,
292
"height": 480,
293
293
**extra_data
294
294
}
295
295
elif aspect_ratio == "9:16":
296
296
extra_data = {
297
"width": 512,
298
"height": 800,
297
"width": 480,
298
"height": 832,
299
299
**extra_data
300
300
}
301
301
return extra_data
@@ -372,7 +372,7 @@ class RaiseErrorMixin():
372
372
elif "error" in data:
373
373
if isinstance(data["error"], str):
374
374
if status is not None:
375
if status in (401, 402):
375
if status == 401:
376
376
raise MissingAuthError(f"Error {status}: {data['error']}")
377
377
raise ResponseError(f"Error {status}: {data['error']}")
378
378
raise ResponseError(data["error"])
@@ -2,17 +2,26 @@ from __future__ import annotations
2
2
3
3
import random
4
4
import string
5
from pathlib import Path
5
6
6
7
from ..typing import Messages, Cookies, AsyncIterator, Iterator
8
from ..tools.files import get_bucket_dir, read_bucket
7
9
from .. import debug
8
10
9
11
def to_string(value) -> str:
10
12
if isinstance(value, str):
11
13
return value
12
14
elif isinstance(value, dict):
13
return value.get("text")
15
if "name" in value:
16
return ""
17
elif "bucket_id" in value:
18
bucket_dir = Path(get_bucket_dir(value.get("bucket_id")))
19
return "".join(read_bucket(bucket_dir))
20
elif value.get("type") == "text":
21
return value.get("text")
22
return ""
14
23
elif isinstance(value, list):
15
return "".join([to_string(v) for v in value if v.get("type") == "text"])
24
return "".join([to_string(v) for v in value if v.get("type", "text") == "text"])
16
25
return str(value)
17
26
18
27
def format_prompt(messages: Messages, add_special_tokens: bool = False, do_continue: bool = False, include_system: bool = True) -> str:
@@ -39,7 +39,7 @@ async def raise_for_status_async(response: Union[StreamResponse, ClientResponse]
39
39
message = "Unknown error (Cloudflare)"
40
40
elif response.status in (429, 402):
41
41
message = "Rate limit"
42
if response.status in (401, 402):
42
if response.status == 401:
43
43
raise MissingAuthError(f"Response {response.status}: {message}")
44
44
if response.status == 403 and is_cloudflare(message):
45
45
raise CloudflareError(f"Response {response.status}: Cloudflare detected")
@@ -66,7 +66,7 @@ def raise_for_status(response: Union[Response, StreamResponse, ClientResponse, R
66
66
message = "Unknown error (Cloudflare)"
67
67
elif response.status_code in (429, 402):
68
68
raise RateLimitError(f"Response {response.status_code}: Rate Limit")
69
if response.status_code in (401, 402):
69
if response.status_code == 401:
70
70
raise MissingAuthError(f"Response {response.status_code}: {message}")
71
71
if response.status_code == 403 and is_cloudflare(response.text):
72
72
raise CloudflareError(f"Response {response.status_code}: Cloudflare detected")
@@ -7,10 +7,10 @@ from pathlib import Path
7
7
8
8
from ..typing import Messages
9
9
from ..image import is_data_an_media, is_data_an_audio, to_input_audio, to_data_uri
10
from .files import get_bucket_dir
10
from .files import get_bucket_dir, read_bucket
11
11
12
12
def render_media(bucket_id: str, name: str, url: str, as_path: bool = False, as_base64: bool = False) -> Union[str, Path]:
13
if (not as_base64 or url.startswith("/")):
13
if (as_base64 or as_path or url.startswith("/")):
14
14
file = Path(get_bucket_dir(bucket_id, "media", name))
15
15
if as_path:
16
16
return file
@@ -19,11 +19,18 @@ def render_media(bucket_id: str, name: str, url: str, as_path: bool = False, as_
19
19
if as_base64:
20
20
return data_base64
21
21
return f"data:{is_data_an_media(data, name)};base64,{data_base64}"
22
return url
22
23
23
24
def render_part(part: dict) -> dict:
24
25
if "type" in part:
25
26
return part
26
27
filename = part.get("name")
28
if (filename is None):
29
bucket_dir = Path(get_bucket_dir(part.get("bucket_id")))
30
return {
31
"type": "text",
32
"text": "".join(read_bucket(bucket_dir))
33
}
27
34
if filename.endswith(".wav") or filename.endswith(".mp3"):
28
35
return {
29
36
"type": "input_audio",
@@ -44,7 +51,7 @@ def merge_media(media: list, messages: list) -> Iterator:
44
51
content = message.get("content")
45
52
if isinstance(content, list):
46
53
for part in content:
47
if "type" not in part:
54
if "type" not in part and "name" in part:
48
55
path = render_media(**part, as_path=True)
49
56
buffer.append((path, os.path.basename(path)))
50
57
elif part.get("type") == "image_url":
@@ -76,7 +83,7 @@ def render_messages(messages: Messages, media: list = None) -> Iterator:
76
83
"image_url": {"url": to_data_uri(media_data)}
77
84
}
78
85
for media_data, filename in media
79
] + ([{"type": "text", "text": message["content"]}] if isinstance(message["content"], str) else [])
86
] + ([{"type": "text", "text": message["content"]}] if isinstance(message["content"], str) else message["content"])
80
87
}
81
88
else:
82
89
yield message