XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Insert buckets as content part Fix copy button for code results Use crypto for uuids in UI

7db18c2a
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

8 个文件 +62 -65
Modified g4f/Provider/hf/HuggingFaceMedia.py +7 -5
@@ -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:
Modified g4f/gui/client/qrcode.html +1 -10
@@ -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, {
Modified g4f/gui/client/static/js/chat.v1.js +25 -37
@@ -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 `[![${item.name}](${item.url})]()`;
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('&lt;i class=&quot;', '<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...`;
Modified g4f/image/__init__.py +4 -4
@@ -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
Modified g4f/providers/base_provider.py +1 -1
@@ -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"])
Modified g4f/providers/helper.py +11 -2
@@ -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:
Modified g4f/requests/raise_for_status.py +2 -2
@@ -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")
Modified g4f/tools/media.py +11 -4
@@ -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