diff options
Diffstat (limited to 'g4f/gui')
-rw-r--r-- | g4f/gui/client/index.html | 5 | ||||
-rw-r--r-- | g4f/gui/client/static/css/style.css | 11 | ||||
-rw-r--r-- | g4f/gui/client/static/js/chat.v1.js | 45 | ||||
-rw-r--r-- | g4f/gui/server/api.py | 12 |
4 files changed, 60 insertions, 13 deletions
diff --git a/g4f/gui/client/index.html b/g4f/gui/client/index.html index b256b0be..3a2197de 100644 --- a/g4f/gui/client/index.html +++ b/g4f/gui/client/index.html @@ -170,6 +170,10 @@ <i class="fa-solid fa-download"></i> <a href="" onclick="return false;">Export Conversations</a> </button> + <button id="showLog"> + <i class="fa-solid fa-terminal"></i> + <a href="" onclick="return false;">Show log</a> + </button> </div> </div> <div class="conversation"> @@ -257,6 +261,7 @@ </div> </div> </div> + <div class="log hidden"></div> </div> <div class="mobile-sidebar"> <i class="fa-solid fa-bars"></i> diff --git a/g4f/gui/client/static/css/style.css b/g4f/gui/client/static/css/style.css index 3ee033ea..8364216a 100644 --- a/g4f/gui/client/static/css/style.css +++ b/g4f/gui/client/static/css/style.css @@ -1051,13 +1051,22 @@ a:-webkit-any-link { padding: var(--inner-gap) var(--inner-gap) var(--inner-gap) 0; } -.settings, .images { +.settings, .log { width: 100%; display: flex; flex-direction: column; overflow: auto; } +.log { + white-space: pre-wrap; + font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; +} + +.log.hidden { + display: none; +} + .settings .paper { flex-direction: column; min-width: 400px; diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js index 11306cdc..51bf8b81 100644 --- a/g4f/gui/client/static/js/chat.v1.js +++ b/g4f/gui/client/static/js/chat.v1.js @@ -19,13 +19,15 @@ const systemPrompt = document.getElementById("systemPrompt"); const settings = document.querySelector(".settings"); const chat = document.querySelector(".conversation"); const album = document.querySelector(".images"); +const log_storage = document.querySelector(".log"); const optionElements = document.querySelectorAll(".settings input, .settings textarea, #model, #model2, #provider") let provider_storage = {}; let message_storage = {}; let controller_storage = {}; -let content_storage = {} +let content_storage = {}; +let error_storage = {}; messageInput.addEventListener("blur", () => { window.scrollTo(0, 0); @@ -256,14 +258,14 @@ const delete_conversations = async () => { const handle_ask = async () => { messageInput.style.height = "82px"; messageInput.focus(); - window.scrollTo(0, 0); + await scroll_to_bottom(); let message = messageInput.value; if (message.length <= 0) { return; } messageInput.value = ""; - count_input() + await count_input() await add_conversation(window.conversation_id, message); if ("text" in fileInput.dataset) { @@ -396,9 +398,12 @@ async function add_message_chunk(message, message_index) { } else if (message.type == "message") { console.error(message.message) } else if (message.type == "error") { - window.error = message.error + error_storage[message_index] = message.error console.error(message.error); content_map.inner.innerHTML += `<p><strong>An error occured:</strong> ${message.error}</p>`; + let p = document.createElement("p"); + p.innerText = message.error; + log_storage.appendChild(p); } else if (message.type == "preview") { content_map.inner.innerHTML = markdown_render(message.preview); } else if (message.type == "content") { @@ -418,6 +423,10 @@ async function add_message_chunk(message, message_index) { content_map.inner.innerHTML = html; content_map.count.innerText = count_words_and_tokens(message_storage[message_index], provider_storage[message_index]?.model); highlight(content_map.inner); + } else if (message.type == "log") { + let p = document.createElement("p"); + p.innerText = message.log; + log_storage.appendChild(p); } window.scrollTo(0, 0); if (message_box.scrollTop >= message_box.scrollHeight - message_box.clientHeight - 100) { @@ -469,7 +478,6 @@ const ask_gpt = async (message_index = -1, message_id) => { `; controller_storage[message_index] = new AbortController(); - let error = false; let content_el = document.getElementById(`gpt_${message_id}`) let content_map = content_storage[message_index] = { @@ -478,8 +486,7 @@ const ask_gpt = async (message_index = -1, message_id) => { count: content_el.querySelector('.count'), } - message_box.scrollTop = message_box.scrollHeight; - window.scrollTo(0, 0); + await scroll_to_bottom(); try { const input = imageInput && imageInput.files.length > 0 ? imageInput : cameraInput; const file = input && input.files.length > 0 ? input.files[0] : null; @@ -501,7 +508,7 @@ const ask_gpt = async (message_index = -1, message_id) => { auto_continue: auto_continue, api_key: api_key }, file, message_index); - if (!error) { + if (!error_storage[message_index]) { html = markdown_render(message_storage[message_index]); content_map.inner.innerHTML = html; highlight(content_map.inner); @@ -513,12 +520,12 @@ const ask_gpt = async (message_index = -1, message_id) => { } catch (e) { console.error(e); if (e.name != "AbortError") { - error = true; + error_storage[message_index] = true; content_map.inner.innerHTML += `<p><strong>An error occured:</strong> ${e}</p>`; } } delete controller_storage[message_index]; - if (!error && message_storage[message_index]) { + if (!error_storage[message_index] && message_storage[message_index]) { const message_provider = message_index in provider_storage ? provider_storage[message_index] : null; await add_message(window.conversation_id, "assistant", message_storage[message_index], message_provider); await safe_load_conversation(window.conversation_id); @@ -526,13 +533,18 @@ const ask_gpt = async (message_index = -1, message_id) => { let cursorDiv = message_box.querySelector(".cursor"); if (cursorDiv) cursorDiv.parentNode.removeChild(cursorDiv); } - window.scrollTo(0, 0); - message_box.scrollTop = message_box.scrollHeight; + await scroll_to_bottom(); await remove_cancel_button(); await register_message_buttons(); await load_conversations(); + regenerate.classList.remove("regenerate-hidden"); }; +async function scroll_to_bottom() { + window.scrollTo(0, 0); + message_box.scrollTop = message_box.scrollHeight; +} + const clear_conversations = async () => { const elements = box_conversations.childNodes; let index = elements.length; @@ -631,6 +643,7 @@ const set_conversation = async (conversation_id) => { await load_conversation(conversation_id); load_conversations(); hide_sidebar(); + log_storage.classList.add("hidden"); }; const new_conversation = async () => { @@ -643,6 +656,7 @@ const new_conversation = async () => { } load_conversations(); hide_sidebar(); + log_storage.classList.add("hidden"); say_hello(); }; @@ -941,6 +955,7 @@ function open_settings() { settings.classList.add("hidden"); chat.classList.remove("hidden"); } + log_storage.classList.add("hidden"); } function open_album() { @@ -1472,3 +1487,9 @@ if (SpeechRecognition) { } }); } + +document.getElementById("showLog").addEventListener("click", ()=> { + log_storage.classList.remove("hidden"); + settings.classList.add("hidden"); + log_storage.scrollTop = log_storage.scrollHeight; +});
\ No newline at end of file diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py index ed8454c3..a6c4bef4 100644 --- a/g4f/gui/server/api.py +++ b/g4f/gui/server/api.py @@ -18,6 +18,7 @@ from g4f.requests.aiohttp import get_connector from g4f.Provider import ProviderType, __providers__, __map__ from g4f.providers.base_provider import ProviderModelMixin, FinishReason from g4f.providers.conversation import BaseConversation +from g4f import debug logger = logging.getLogger(__name__) @@ -140,6 +141,13 @@ class Api: } def _create_response_stream(self, kwargs: dict, conversation_id: str, provider: str) -> Iterator: + if debug.logging: + logs = [] + print_callback = debug.log_handler + def log_handler(text: str): + logs.append(text) + print_callback(text) + debug.log_handler = log_handler try: result = ChatCompletion.create(**kwargs) first = True @@ -168,6 +176,10 @@ class Api: yield self._format_json("content", str(ImageResponse(images, chunk.alt))) elif not isinstance(chunk, FinishReason): yield self._format_json("content", str(chunk)) + if logs: + for log in logs: + yield self._format_json("log", str(log)) + logs = [] except Exception as e: logger.exception(e) yield self._format_json('error', get_error_message(e)) |