diff options
author | H Lohaus <hlohaus@users.noreply.github.com> | 2024-11-22 01:50:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-22 01:50:48 +0100 |
commit | e8bd24a25bb8737c4f6ef8ba656e74a58e74336a (patch) | |
tree | aa45d15f59adaf85c8f4154f44ccbc17afb0a0bd /g4f/gui | |
parent | Improve slim docker image example, clean up OpenaiChat provider (#2397) (diff) | |
download | gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar.gz gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar.bz2 gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar.lz gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar.xz gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.tar.zst gpt4free-e8bd24a25bb8737c4f6ef8ba656e74a58e74336a.zip |
Diffstat (limited to '')
-rw-r--r-- | g4f/gui/client/index.html | 3 | ||||
-rw-r--r-- | g4f/gui/client/static/css/style.css | 19 | ||||
-rw-r--r-- | g4f/gui/client/static/js/chat.v1.js | 66 | ||||
-rw-r--r-- | g4f/gui/server/api.py | 13 | ||||
-rw-r--r-- | g4f/gui/server/backend.py | 24 |
5 files changed, 82 insertions, 43 deletions
diff --git a/g4f/gui/client/index.html b/g4f/gui/client/index.html index 116509d8..8cbcd578 100644 --- a/g4f/gui/client/index.html +++ b/g4f/gui/client/index.html @@ -191,6 +191,9 @@ <button class="slide-systemPrompt"> <i class="fa-solid fa-angles-up"></i> </button> + <div class="media_player"> + <i class="fa-regular fa-x"></i> + </div> <div class="toolbar"> <div id="input-count" class=""> <button class="hide-input"> diff --git a/g4f/gui/client/static/css/style.css b/g4f/gui/client/static/css/style.css index b7ec00b9..57b75bae 100644 --- a/g4f/gui/client/static/css/style.css +++ b/g4f/gui/client/static/css/style.css @@ -434,15 +434,28 @@ body { font-size: 12px; } -.message audio { +.media_player { display: none; - max-width: 400px; } -.message audio.show { +.media_player audio { + right: 28px; + position: absolute; + top: -4px; + z-index: 900; +} + +.media_player.show { display: block; } +.media_player .fa-x { + position: absolute; + right: 8px; + top: 8px; + z-index: 1000; +} + .count_total { font-size: 12px; padding-left: 25px; diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js index 8127fb9d..fd9cc50e 100644 --- a/g4f/gui/client/static/js/chat.v1.js +++ b/g4f/gui/client/static/js/chat.v1.js @@ -44,17 +44,18 @@ appStorage = window.localStorage || { removeItem: (key) => delete self[key], length: 0 } - -const markdown = window.markdownit(); -const markdown_render = (content) => { - return markdown.render(content - .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "") - .replaceAll(/<img data-prompt="[^>]+">/gm, "") - ) - .replaceAll("<a href=", '<a target="_blank" href=') - .replaceAll('<code>', '<code class="language-plaintext">') +let markdown_render = () => null; +if (window.markdownit) { + const markdown = window.markdownit(); + markdown_render = (content) => { + return markdown.render(content + .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "") + .replaceAll(/<img data-prompt="[^>]+">/gm, "") + ) + .replaceAll("<a href=", '<a target="_blank" href=') + .replaceAll('<code>', '<code class="language-plaintext">') + } } - function filter_message(text) { return text.replaceAll( /<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, "" @@ -135,10 +136,21 @@ const register_message_buttons = async () => { if (!("click" in el.dataset)) { el.dataset.click = "true"; el.addEventListener("click", async () => { - const content_el = el.parentElement.parentElement; - const audio = content_el.querySelector("audio"); - if (audio) { - audio.classList.add("show"); + const message_el = el.parentElement.parentElement.parentElement; + let audio; + if (message_el.dataset.synthesize_url) { + el.classList.add("active"); + setTimeout(()=>el.classList.remove("active"), 2000); + const media_player = document.querySelector(".media_player"); + if (!media_player.classList.contains("show")) { + media_player.classList.add("show"); + audio = new Audio(message_el.dataset.synthesize_url); + audio.controls = true; + media_player.appendChild(audio); + } else { + audio = media_player.querySelector("audio"); + audio.src = message_el.dataset.synthesize_url; + } audio.play(); return; } @@ -163,7 +175,7 @@ const register_message_buttons = async () => { el.dataset.running = true; el.classList.add("blink") el.classList.add("active") - const message_el = content_el.parentElement; + let speechText = await get_message(window.conversation_id, message_el.dataset.index); speechText = speechText.replaceAll(/([^0-9])\./gm, "$1.;"); @@ -351,6 +363,13 @@ stop_generating.addEventListener("click", async () => { await load_conversation(window.conversation_id, false); }); +document.querySelector(".media_player .fa-x").addEventListener("click", ()=>{ + const media_player = document.querySelector(".media_player"); + media_player.classList.remove("show"); + const audio = document.querySelector(".media_player audio"); + media_player.removeChild(audio); +}); + const prepare_messages = (messages, message_index = -1) => { if (message_index >= 0) { messages = messages.filter((_, index) => message_index >= index); @@ -726,17 +745,17 @@ const load_conversation = async (conversation_id, scroll=true) => { ${item.provider.model ? ' with ' + item.provider.model : ''} </div> ` : ""; - let audio = ""; + let synthesize_params = {text: item.content} + let synthesize_provider = "Gemini"; if (item.synthesize) { - const synthesize_params = (new URLSearchParams(item.synthesize.data)).toString(); - audio = ` - <audio controls preload="none"> - <source src="/backend-api/v2/synthesize/${item.synthesize.provider}?${synthesize_params}" type="audio/mpeg"> - </audio> - `; + synthesize_params = item.synthesize.data + synthesize_provider = item.synthesize.provider; } + synthesize_params = (new URLSearchParams(synthesize_params)).toString(); + let synthesize_url = `/backend-api/v2/synthesize/${synthesize_provider}?${synthesize_params}`; + elements += ` - <div class="message${item.regenerate ? " regenerate": ""}" data-index="${i}"> + <div class="message${item.regenerate ? " regenerate": ""}" data-index="${i}" data-synthesize_url="${synthesize_url}"> <div class="${item.role}"> ${item.role == "assistant" ? gpt_image : user_image} <i class="fa-solid fa-xmark"></i> @@ -748,7 +767,6 @@ const load_conversation = async (conversation_id, scroll=true) => { <div class="content"> ${provider} <div class="content_inner">${markdown_render(item.content)}</div> - ${audio} <div class="count"> ${count_words_and_tokens(item.content, next_provider?.model)} <i class="fa-solid fa-volume-high"></i> diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py index 0c32bea5..ecf7bc54 100644 --- a/g4f/gui/server/api.py +++ b/g4f/gui/server/api.py @@ -140,13 +140,12 @@ class Api: } def _create_response_stream(self, kwargs: dict, conversation_id: str, provider: str, download_images: bool = True) -> Iterator: - if debug.logging: - debug.logs = [] - print_callback = debug.log_handler - def log_handler(text: str): - debug.logs.append(text) - print_callback(text) - debug.log_handler = log_handler + debug.logs = [] + print_callback = debug.log_handler + def log_handler(text: str): + debug.logs.append(text) + print_callback(text) + debug.log_handler = log_handler try: result = ChatCompletion.create(**kwargs) first = True diff --git a/g4f/gui/server/backend.py b/g4f/gui/server/backend.py index 102c5685..3dcae546 100644 --- a/g4f/gui/server/backend.py +++ b/g4f/gui/server/backend.py @@ -1,6 +1,8 @@ import json import flask import os +import logging +import asyncio from flask import request, Flask from typing import Generator from werkzeug.utils import secure_filename @@ -12,6 +14,8 @@ from g4f.errors import ProviderNotFoundError from g4f.cookies import get_cookies_dir from .api import Api +logger = logging.getLogger(__name__) + def safe_iter_generator(generator: Generator) -> Generator: start = next(generator) def iter_generator(): @@ -127,15 +131,17 @@ class Backend_Api(Api): return "Provider not found", 404 if not hasattr(provider_handler, "synthesize"): return "Provider doesn't support synthesize", 500 - try: - response_generator = provider_handler.synthesize({**request.args}) - if hasattr(response_generator, "__aiter__"): - response_generator = to_sync_generator(response_generator) - response = flask.Response(safe_iter_generator(response_generator), content_type="audio/mpeg") - response.headers['Cache-Control'] = "max-age=604800" - return response - except Exception as e: - return f"{e.__class__.__name__}: {e}", 500 + response_data = provider_handler.synthesize({**request.args}) + if asyncio.iscoroutinefunction(provider_handler.synthesize): + response_data = asyncio.run(response_data) + else: + if hasattr(response_data, "__aiter__"): + response_data = to_sync_generator(response_data) + response_data = safe_iter_generator(response_data) + content_type = getattr(provider_handler, "synthesize_content_type", "application/octet-stream") + response = flask.Response(response_data, content_type=content_type) + response.headers['Cache-Control'] = "max-age=604800" + return response def get_provider_models(self, provider: str): api_key = None if request.authorization is None else request.authorization.token |