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

This commit is contained in:
Heiner Lohaus 2024-05-20 01:28:08 +02:00
parent c8d61a08b7
commit 80b2e9b807
10 changed files with 109 additions and 37 deletions

3
.gitignore vendored
View File

@ -64,4 +64,5 @@ dist.py
x.txt x.txt
bench.py bench.py
to-reverse.txt to-reverse.txt
g4f/Provider/OpenaiChat2.py g4f/Provider/OpenaiChat2.py
generated_images/

View File

@ -92,7 +92,12 @@ As per the survey, here is a list of improvements to come
```sh ```sh
docker pull hlohaus789/g4f docker pull hlohaus789/g4f
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 docker run \
-p 8080:8080 -p 1337:1337 -p 7900:7900 \
--shm-size="2g" \
-v ${PWD}/har_and_cookies:/app/har_and_cookies \
-v ${PWD}/generated_images:/app/generated_images \
hlohaus789/g4f:latest
``` ```
3. **Access the Client:** 3. **Access the Client:**

View File

@ -4,7 +4,6 @@ import os
import json import json
import random import random
import re import re
import base64
from aiohttp import ClientSession, BaseConnector from aiohttp import ClientSession, BaseConnector
@ -193,14 +192,10 @@ class Gemini(AsyncGeneratorProvider):
yield content yield content
if image_prompt: if image_prompt:
images = [image[0][3][3] for image in response_part[4][0][12][7][0]] images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
resolved_images = []
if response_format == "b64_json": if response_format == "b64_json":
for image in images: yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
async with client.get(image) as response:
data = base64.b64encode(await response.content.read()).decode()
resolved_images.append(data)
yield ImageDataResponse(resolved_images, image_prompt)
else: else:
resolved_images = []
preview = [] preview = []
for image in images: for image in images:
async with client.get(image, allow_redirects=False) as fetch: async with client.get(image, allow_redirects=False) as fetch:

View File

@ -171,7 +171,8 @@ async def iter_image_response(
if isinstance(chunk, ImageProviderResponse): if isinstance(chunk, ImageProviderResponse):
if response_format == "b64_json": if response_format == "b64_json":
async with ClientSession( async with ClientSession(
connector=get_connector(connector, proxy) connector=get_connector(connector, proxy),
cookies=chunk.options.get("cookies")
) as session: ) as session:
async def fetch_image(image): async def fetch_image(image):
async with session.get(image) as response: async with session.get(image) as response:

View File

@ -32,10 +32,10 @@
<script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async> <script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
import mistralTokenizer from "mistral-tokenizer-js" import mistralTokenizer from "mistral-tokenizer-js"
</script> </script>
<script type="module" src="https://belladoreai.github.io/llama-tokenizer-js/llama-tokenizer.js" async> <script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
import llamaTokenizer from "llama-tokenizer-js" import llamaTokenizer from "llama-tokenizer-js"
</script> </script>
<script src="https://unpkg.com/gpt-tokenizer/dist/cl100k_base.js" async></script> <script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
<script src="/static/js/text_to_speech/index.js" async></script> <script src="/static/js/text_to_speech/index.js" async></script>
<!-- <!--
<script src="/static/js/whisper-web/index.js" async></script> <script src="/static/js/whisper-web/index.js" async></script>

View File

@ -265,6 +265,14 @@ body {
padding-bottom: 0; padding-bottom: 0;
} }
.message.print {
height: 100%;
position: absolute;
background-color: #fff;
z-index: 100;
top: 0;
}
.message.regenerate { .message.regenerate {
opacity: 0.75; opacity: 0.75;
} }
@ -339,14 +347,14 @@ body {
flex-wrap: wrap; flex-wrap: wrap;
} }
.message .content, .message .content_inner,
.message .content a:link, .message .content_inner a:link,
.message .content a:visited{ .message .content_inner a:visited{
font-size: 15px; font-size: 15px;
line-height: 1.3; line-height: 1.3;
color: var(--colour-3); color: var(--colour-3);
} }
.message .content pre{ .message .content_inner pre{
white-space: pre-wrap; white-space: pre-wrap;
} }
@ -389,19 +397,19 @@ body {
.message .count .fa-clipboard, .message .count .fa-clipboard,
.message .count .fa-volume-high, .message .count .fa-volume-high,
.message .count .fa-rotate { .message .count .fa-rotate,
.message .count .fa-print {
z-index: 1000; z-index: 1000;
cursor: pointer; cursor: pointer;
} }
.message .count .fa-clipboard { .message .count .fa-clipboard,
.message .count .fa-whatsapp {
color: var(--colour-3); color: var(--colour-3);
} }
.message .count .fa-clipboard.clicked { .message .count .fa-clipboard.clicked,
color: var(--accent); .message .count .fa-print.clicked,
}
.message .count .fa-volume-high.active { .message .count .fa-volume-high.active {
color: var(--accent); color: var(--accent);
} }
@ -1121,4 +1129,18 @@ a:-webkit-any-link {
50% { 50% {
opacity: 0; opacity: 0;
} }
}
@media print {
#systemPrompt:placeholder-shown,
.conversations,
.conversation .user-input,
.conversation .buttons,
.conversation .toolbar,
.conversation .slide-systemPrompt,
.message .count i,
.message .assistant,
.message .user {
display: none;
}
} }

View File

@ -192,6 +192,26 @@ const register_message_buttons = async () => {
}) })
} }
}); });
document.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => {
if (!el.parentElement.href) {
const text = el.parentElement.parentElement.parentElement.innerText;
el.parentElement.href = `https://wa.me/?text=${encodeURIComponent(text)}`;
}
});
document.querySelectorAll(".message .fa-print").forEach(async (el) => {
if (!("click" in el.dataset)) {
el.dataset.click = "true";
el.addEventListener("click", async () => {
const message_el = el.parentElement.parentElement.parentElement;
el.classList.add("clicked");
message_box.scrollTop = 0;
message_el.classList.add("print");
setTimeout(() => el.classList.remove("clicked"), 1000);
setTimeout(() => message_el.classList.remove("print"), 1000);
window.print()
})
}
});
} }
const delete_conversations = async () => { const delete_conversations = async () => {
@ -253,6 +273,8 @@ const handle_ask = async () => {
${count_words_and_tokens(message, get_selected_model())} ${count_words_and_tokens(message, get_selected_model())}
<i class="fa-solid fa-volume-high"></i> <i class="fa-solid fa-volume-high"></i>
<i class="fa-regular fa-clipboard"></i> <i class="fa-regular fa-clipboard"></i>
<a><i class="fa-brands fa-whatsapp"></i></a>
<i class="fa-solid fa-print"></i>
</div> </div>
</div> </div>
</div> </div>
@ -625,6 +647,8 @@ const load_conversation = async (conversation_id, scroll=true) => {
${count_words_and_tokens(item.content, next_provider?.model)} ${count_words_and_tokens(item.content, next_provider?.model)}
<i class="fa-solid fa-volume-high"></i> <i class="fa-solid fa-volume-high"></i>
<i class="fa-regular fa-clipboard"></i> <i class="fa-regular fa-clipboard"></i>
<a><i class="fa-brands fa-whatsapp"></i></a>
<i class="fa-solid fa-print"></i>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,18 +1,27 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import json import os
from typing import Iterator import os.path
import uuid
import asyncio
import time
from aiohttp import ClientSession
from typing import Iterator, Optional
from flask import send_from_directory
from g4f import version, models from g4f import version, models
from g4f import get_last_provider, ChatCompletion from g4f import get_last_provider, ChatCompletion
from g4f.errors import VersionNotFoundError from g4f.errors import VersionNotFoundError
from g4f.image import ImagePreview from g4f.typing import Cookies
from g4f.image import ImagePreview, ImageResponse, is_accepted_format
from g4f.requests.aiohttp import get_connector
from g4f.Provider import ProviderType, __providers__, __map__ from g4f.Provider import ProviderType, __providers__, __map__
from g4f.providers.base_provider import ProviderModelMixin, FinishReason from g4f.providers.base_provider import ProviderModelMixin, FinishReason
from g4f.providers.conversation import BaseConversation from g4f.providers.conversation import BaseConversation
conversations: dict[dict[str, BaseConversation]] = {} conversations: dict[dict[str, BaseConversation]] = {}
images_dir = "./generated_images"
class Api(): class Api():
@ -110,14 +119,8 @@ class Api():
"latest_version": version.utils.latest_version, "latest_version": version.utils.latest_version,
} }
def generate_title(self): def serve_images(self, name):
""" return send_from_directory(os.path.abspath(images_dir), name)
Generates and returns a title based on the request data.
Returns:
dict: A dictionary with the generated title.
"""
return {'title': ''}
def _prepare_conversation_kwargs(self, json_data: dict, kwargs: dict): def _prepare_conversation_kwargs(self, json_data: dict, kwargs: dict):
""" """
@ -185,6 +188,27 @@ class Api():
yield self._format_json("message", get_error_message(chunk)) yield self._format_json("message", get_error_message(chunk))
elif isinstance(chunk, ImagePreview): elif isinstance(chunk, ImagePreview):
yield self._format_json("preview", chunk.to_string()) yield self._format_json("preview", chunk.to_string())
elif isinstance(chunk, ImageResponse):
async def copy_images(images: list[str], cookies: Optional[Cookies] = None):
async with ClientSession(
connector=get_connector(None, os.environ.get("G4F_PROXY")),
cookies=cookies
) as session:
async def copy_image(image):
async with session.get(image) as response:
target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
with open(target, "wb") as f:
async for chunk in response.content.iter_any():
f.write(chunk)
with open(target, "rb") as f:
extension = is_accepted_format(f.read(12)).split("/")[-1]
extension = "jpg" if extension == "jpeg" else extension
new_target = f"{target}.{extension}"
os.rename(target, new_target)
return f"/images/{os.path.basename(new_target)}"
return await asyncio.gather(*[copy_image(image) for image in images])
images = asyncio.run(copy_images(chunk.get_list(), chunk.options.get("cookies")))
yield self._format_json("content", str(ImageResponse(images, chunk.alt)))
elif not isinstance(chunk, FinishReason): elif not isinstance(chunk, FinishReason):
yield self._format_json("content", str(chunk)) yield self._format_json("content", str(chunk))
except Exception as e: except Exception as e:

View File

@ -47,13 +47,13 @@ class Backend_Api(Api):
'function': self.handle_conversation, 'function': self.handle_conversation,
'methods': ['POST'] 'methods': ['POST']
}, },
'/backend-api/v2/gen.set.summarize:title': {
'function': self.generate_title,
'methods': ['POST']
},
'/backend-api/v2/error': { '/backend-api/v2/error': {
'function': self.handle_error, 'function': self.handle_error,
'methods': ['POST'] 'methods': ['POST']
},
'/images/<path:name>': {
'function': self.serve_images,
'methods': ['GET']
} }
} }

View File