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

XFEstudio/gpt4free

Add some options to messages in gui, saves generated images local

80b2e9b8
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

10 个文件 +109 -37
Modified .gitignore +2 -1
@@ -64,4 +64,5 @@ dist.py
64 64 x.txt
65 65 bench.py
66 66 to-reverse.txt
67 g4f/Provider/OpenaiChat2.py
67 g4f/Provider/OpenaiChat2.py
68 generated_images/
Modified README.md +6 -1
@@ -92,7 +92,12 @@ As per the survey, here is a list of improvements to come
92 92
93 93 ```sh
94 94 docker pull hlohaus789/g4f
95 docker run -p 8080:8080 -p 1337:1337 -p 7900:7900 --shm-size="2g" -v ${PWD}/har_and_cookies:/app/har_and_cookies hlohaus789/g4f:latest
95 docker run \
96 -p 8080:8080 -p 1337:1337 -p 7900:7900 \
97 --shm-size="2g" \
98 -v ${PWD}/har_and_cookies:/app/har_and_cookies \
99 -v ${PWD}/generated_images:/app/generated_images \
100 hlohaus789/g4f:latest
96 101 ```
97 102
98 103 3. **Access the Client:**
Modified g4f/Provider/needs_auth/Gemini.py +2 -7
@@ -4,7 +4,6 @@ import os
4 4 import json
5 5 import random
6 6 import re
7 import base64
8 7
9 8 from aiohttp import ClientSession, BaseConnector
10 9
@@ -193,14 +192,10 @@ class Gemini(AsyncGeneratorProvider):
193 192 yield content
194 193 if image_prompt:
195 194 images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
196 resolved_images = []
197 195 if response_format == "b64_json":
198 for image in images:
199 async with client.get(image) as response:
200 data = base64.b64encode(await response.content.read()).decode()
201 resolved_images.append(data)
202 yield ImageDataResponse(resolved_images, image_prompt)
196 yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
203 197 else:
198 resolved_images = []
204 199 preview = []
205 200 for image in images:
206 201 async with client.get(image, allow_redirects=False) as fetch:
Modified g4f/client/async_client.py +2 -1
@@ -171,7 +171,8 @@ async def iter_image_response(
171 171 if isinstance(chunk, ImageProviderResponse):
172 172 if response_format == "b64_json":
173 173 async with ClientSession(
174 connector=get_connector(connector, proxy)
174 connector=get_connector(connector, proxy),
175 cookies=chunk.options.get("cookies")
175 176 ) as session:
176 177 async def fetch_image(image):
177 178 async with session.get(image) as response:
Modified g4f/gui/client/index.html +2 -2
@@ -32,10 +32,10 @@
32 32 <script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
33 33 import mistralTokenizer from "mistral-tokenizer-js"
34 34 </script>
35 <script type="module" src="https://belladoreai.github.io/llama-tokenizer-js/llama-tokenizer.js" async>
35 <script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
36 36 import llamaTokenizer from "llama-tokenizer-js"
37 37 </script>
38 <script src="https://unpkg.com/gpt-tokenizer/dist/cl100k_base.js" async></script>
38 <script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
39 39 <script src="/static/js/text_to_speech/index.js" async></script>
40 40 <!--
41 41 <script src="/static/js/whisper-web/index.js" async></script>
Modified g4f/gui/client/static/css/style.css +32 -10
@@ -265,6 +265,14 @@ body {
265 265 padding-bottom: 0;
266 266 }
267 267
268 .message.print {
269 height: 100%;
270 position: absolute;
271 background-color: #fff;
272 z-index: 100;
273 top: 0;
274 }
275
268 276 .message.regenerate {
269 277 opacity: 0.75;
270 278 }
@@ -339,14 +347,14 @@ body {
339 347 flex-wrap: wrap;
340 348 }
341 349
342 .message .content,
343 .message .content a:link,
344 .message .content a:visited{
350 .message .content_inner,
351 .message .content_inner a:link,
352 .message .content_inner a:visited{
345 353 font-size: 15px;
346 354 line-height: 1.3;
347 355 color: var(--colour-3);
348 356 }
349 .message .content pre{
357 .message .content_inner pre{
350 358 white-space: pre-wrap;
351 359 }
352 360
@@ -389,19 +397,19 @@ body {
389 397
390 398 .message .count .fa-clipboard,
391 399 .message .count .fa-volume-high,
392 .message .count .fa-rotate {
400 .message .count .fa-rotate,
401 .message .count .fa-print {
393 402 z-index: 1000;
394 403 cursor: pointer;
395 404 }
396 405
397 .message .count .fa-clipboard {
406 .message .count .fa-clipboard,
407 .message .count .fa-whatsapp {
398 408 color: var(--colour-3);
399 409 }
400 410
401 .message .count .fa-clipboard.clicked {
402 color: var(--accent);
403 }
404
411 .message .count .fa-clipboard.clicked,
412 .message .count .fa-print.clicked,
405 413 .message .count .fa-volume-high.active {
406 414 color: var(--accent);
407 415 }
@@ -1121,4 +1129,18 @@ a:-webkit-any-link {
1121 1129 50% {
1122 1130 opacity: 0;
1123 1131 }
1132 }
1133
1134 @media print {
1135 #systemPrompt:placeholder-shown,
1136 .conversations,
1137 .conversation .user-input,
1138 .conversation .buttons,
1139 .conversation .toolbar,
1140 .conversation .slide-systemPrompt,
1141 .message .count i,
1142 .message .assistant,
1143 .message .user {
1144 display: none;
1145 }
1124 1146 }
Modified g4f/gui/client/static/js/chat.v1.js +24 -0
@@ -192,6 +192,26 @@ const register_message_buttons = async () => {
192 192 })
193 193 }
194 194 });
195 document.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => {
196 if (!el.parentElement.href) {
197 const text = el.parentElement.parentElement.parentElement.innerText;
198 el.parentElement.href = `https://wa.me/?text=${encodeURIComponent(text)}`;
199 }
200 });
201 document.querySelectorAll(".message .fa-print").forEach(async (el) => {
202 if (!("click" in el.dataset)) {
203 el.dataset.click = "true";
204 el.addEventListener("click", async () => {
205 const message_el = el.parentElement.parentElement.parentElement;
206 el.classList.add("clicked");
207 message_box.scrollTop = 0;
208 message_el.classList.add("print");
209 setTimeout(() => el.classList.remove("clicked"), 1000);
210 setTimeout(() => message_el.classList.remove("print"), 1000);
211 window.print()
212 })
213 }
214 });
195 215 }
196 216
197 217 const delete_conversations = async () => {
@@ -253,6 +273,8 @@ const handle_ask = async () => {
253 273 ${count_words_and_tokens(message, get_selected_model())}
254 274 <i class="fa-solid fa-volume-high"></i>
255 275 <i class="fa-regular fa-clipboard"></i>
276 <a><i class="fa-brands fa-whatsapp"></i></a>
277 <i class="fa-solid fa-print"></i>
256 278 </div>
257 279 </div>
258 280 </div>
@@ -625,6 +647,8 @@ const load_conversation = async (conversation_id, scroll=true) => {
625 647 ${count_words_and_tokens(item.content, next_provider?.model)}
626 648 <i class="fa-solid fa-volume-high"></i>
627 649 <i class="fa-regular fa-clipboard"></i>
650 <a><i class="fa-brands fa-whatsapp"></i></a>
651 <i class="fa-solid fa-print"></i>
628 652 </div>
629 653 </div>
630 654 </div>
Modified g4f/gui/server/api.py +35 -11
@@ -1,18 +1,27 @@
1 1 from __future__ import annotations
2 2
3 3 import logging
4 import json
5 from typing import Iterator
4 import os
5 import os.path
6 import uuid
7 import asyncio
8 import time
9 from aiohttp import ClientSession
10 from typing import Iterator, Optional
11 from flask import send_from_directory
6 12
7 13 from g4f import version, models
8 14 from g4f import get_last_provider, ChatCompletion
9 15 from g4f.errors import VersionNotFoundError
10 from g4f.image import ImagePreview
16 from g4f.typing import Cookies
17 from g4f.image import ImagePreview, ImageResponse, is_accepted_format
18 from g4f.requests.aiohttp import get_connector
11 19 from g4f.Provider import ProviderType, __providers__, __map__
12 20 from g4f.providers.base_provider import ProviderModelMixin, FinishReason
13 21 from g4f.providers.conversation import BaseConversation
14 22
15 23 conversations: dict[dict[str, BaseConversation]] = {}
24 images_dir = "./generated_images"
16 25
17 26 class Api():
18 27
@@ -110,14 +119,8 @@ class Api():
110 119 "latest_version": version.utils.latest_version,
111 120 }
112 121
113 def generate_title(self):
114 """
115 Generates and returns a title based on the request data.
116
117 Returns:
118 dict: A dictionary with the generated title.
119 """
120 return {'title': ''}
122 def serve_images(self, name):
123 return send_from_directory(os.path.abspath(images_dir), name)
121 124
122 125 def _prepare_conversation_kwargs(self, json_data: dict, kwargs: dict):
123 126 """
@@ -185,6 +188,27 @@ class Api():
185 188 yield self._format_json("message", get_error_message(chunk))
186 189 elif isinstance(chunk, ImagePreview):
187 190 yield self._format_json("preview", chunk.to_string())
191 elif isinstance(chunk, ImageResponse):
192 async def copy_images(images: list[str], cookies: Optional[Cookies] = None):
193 async with ClientSession(
194 connector=get_connector(None, os.environ.get("G4F_PROXY")),
195 cookies=cookies
196 ) as session:
197 async def copy_image(image):
198 async with session.get(image) as response:
199 target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
200 with open(target, "wb") as f:
201 async for chunk in response.content.iter_any():
202 f.write(chunk)
203 with open(target, "rb") as f:
204 extension = is_accepted_format(f.read(12)).split("/")[-1]
205 extension = "jpg" if extension == "jpeg" else extension
206 new_target = f"{target}.{extension}"
207 os.rename(target, new_target)
208 return f"/images/{os.path.basename(new_target)}"
209 return await asyncio.gather(*[copy_image(image) for image in images])
210 images = asyncio.run(copy_images(chunk.get_list(), chunk.options.get("cookies")))
211 yield self._format_json("content", str(ImageResponse(images, chunk.alt)))
188 212 elif not isinstance(chunk, FinishReason):
189 213 yield self._format_json("content", str(chunk))
190 214 except Exception as e:
Modified g4f/gui/server/backend.py +4 -4
@@ -47,13 +47,13 @@ class Backend_Api(Api):
47 47 'function': self.handle_conversation,
48 48 'methods': ['POST']
49 49 },
50 '/backend-api/v2/gen.set.summarize:title': {
51 'function': self.generate_title,
52 'methods': ['POST']
53 },
54 50 '/backend-api/v2/error': {
55 51 'function': self.handle_error,
56 52 'methods': ['POST']
53 },
54 '/images/<path:name>': {
55 'function': self.serve_images,
56 'methods': ['GET']
57 57 }
58 58 }
59 59
Added generated_images/.gitkeep +0 -0
此文件没有可显示的逐行差异。