diff options
author | H Lohaus <hlohaus@users.noreply.github.com> | 2024-01-13 15:48:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-13 15:48:38 +0100 |
commit | 467f2a6fc67e6c5a6cf996da961dc1f68bd7d456 (patch) | |
tree | 70d06c98503a235d8e6727a9ea4537fea4330f8c /g4f/gui | |
parent | ~ (diff) | |
parent | Support upload image in gui (diff) | |
download | gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar.gz gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar.bz2 gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar.lz gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar.xz gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.tar.zst gpt4free-467f2a6fc67e6c5a6cf996da961dc1f68bd7d456.zip |
Diffstat (limited to '')
-rw-r--r-- | g4f/gui/client/css/style.css | 34 | ||||
-rw-r--r-- | g4f/gui/client/html/index.html | 93 | ||||
-rw-r--r-- | g4f/gui/client/js/chat.v1.js | 96 | ||||
-rw-r--r-- | g4f/gui/server/backend.py | 58 |
4 files changed, 137 insertions, 144 deletions
diff --git a/g4f/gui/client/css/style.css b/g4f/gui/client/css/style.css index 3e2d6d6f..59464272 100644 --- a/g4f/gui/client/css/style.css +++ b/g4f/gui/client/css/style.css @@ -217,7 +217,6 @@ body { } .message { - width: 100%; overflow-wrap: break-word; display: flex; @@ -302,10 +301,14 @@ body { line-height: 1.3; color: var(--colour-3); } -.message .content pre { +.message .content pre{ white-space: pre-wrap; } +.message .content img{ + max-width: 400px; +} + .message .user i { position: absolute; bottom: -6px; @@ -401,13 +404,28 @@ body { display: none; } -input[type="checkbox"] { +#image { + display: none; +} + +label[for="image"]:has(> input:valid){ + color: var(--accent); +} + +label[for="image"] { + cursor: pointer; + position: absolute; + top: 10px; + left: 10px; +} + +.buttons input[type="checkbox"] { height: 0; width: 0; display: none; } -label { +.buttons label { cursor: pointer; text-indent: -9999px; width: 50px; @@ -424,7 +442,7 @@ label { transition: 0.33s; } -label:after { +.buttons label:after { content: ""; position: absolute; top: 50%; @@ -437,11 +455,11 @@ label:after { transition: 0.33s; } -input:checked+label { - background: var(--blur-border); +.buttons input:checked+label { + background: var(--accent); } -input:checked+label:after { +.buttons input:checked+label:after { left: calc(100% - 5px - 20px); } diff --git a/g4f/gui/client/html/index.html b/g4f/gui/client/html/index.html index bc41bd45..3f2bb0c0 100644 --- a/g4f/gui/client/html/index.html +++ b/g4f/gui/client/html/index.html @@ -36,7 +36,8 @@ #message-input { margin-right: 30px; - height: 80px; + height: 82px; + margin-left: 20px; } #message-input::-webkit-scrollbar { @@ -113,6 +114,10 @@ <div class="box input-box"> <textarea id="message-input" placeholder="Ask a question" cols="30" rows="10" style="white-space: pre-wrap;resize: none;"></textarea> + <label for="image" title="Works only with Bing and OpenaiChat"> + <input type="file" id="image" name="image" accept="image/png, image/gif, image/jpeg" required/> + <i class="fa-regular fa-image"></i> + </label> <div id="send-button"> <i class="fa-solid fa-paper-plane-top"></i> </div> @@ -120,52 +125,7 @@ </div> <div class="buttons"> <div class="field"> - <input type="checkbox" id="switch" /> - <label for="switch"></label> - <span class="about">Web Access</span> - </div> - <div class="field"> - <input type="checkbox" id="patch" /> - <label for="patch" title="Works only with Bing and some other providers"></label> - <span class="about">Image Generator</span> - </div> - <div class="field"> - <select name="model" id="model"> - <option value="gpt-3.5-turbo" selected="">gpt-3.5-turbo</option> - <option value="gpt-3.5-turbo-0613">gpt-3.5-turbo-0613</option> - <option value="gpt-3.5-turbo-16k">gpt-3.5-turbo-16k</option> - <option value="gpt-3.5-turbo-16k-0613">gpt-3.5-turbo-16k-0613</option> - <option value="gpt-4">gpt-4</option> - <option value="gpt-4-0613">gpt-4-0613</option> - <option value="gpt-4-32k">gpt-4-32k</option> - <option value="gpt-4-32k-0613">gpt-4-32k-0613</option> - <option value="palm2">palm2</option> - <option value="palm">palm</option> - <option value="google">google</option> - <option value="google-bard">google-bard</option> - <option value="google-palm">google-palm</option> - <option value="bard">bard</option> - <option value="llama2-7b">llama2-7b</option> - <option value="llama2-13b">llama2-13b</option> - <option value="llama2-70b">llama2-70b</option> - <option value="command-nightly">command-nightly</option> - <option value="gpt-neox-20b">gpt-neox-20b</option> - <option value="santacoder">santacoder</option> - <option value="bloom">bloom</option> - <option value="flan-t5-xxl">flan-t5-xxl</option> - <option value="code-davinci-002">code-davinci-002</option> - <option value="text-ada-001">text-ada-001</option> - <option value="text-babbage-001">text-babbage-001</option> - <option value="text-curie-001">text-curie-001</option> - <option value="text-davinci-002">text-davinci-002</option> - <option value="text-davinci-003">text-davinci-003</option> - <option value="llama70b-v2-chat">llama70b-v2-chat</option> - <option value="llama13b-v2-chat">llama13b-v2-chat</option> - <option value="llama7b-v2-chat">llama7b-v2-chat</option> - <option value="oasst-sft-1-pythia-12b">oasst-sft-1-pythia-12b</option> - <option value="oasst-sft-4-pythia-12b-epoch-3.5">oasst-sft-4-pythia-12b-epoch-3.5</option> - <option value="command-light-nightly">command-light-nightly</option> - </select> + <select name="model" id="model"></select> </div> <div class="field"> <select name="jailbreak" id="jailbreak" style="display: none;"> @@ -178,36 +138,19 @@ <option value="gpt-evil-1.0">evil 1.0</option> </select> <div class="field"> - <select name="provider" id="provider"> - <option value="g4f.Provider.Auto" selected>Set Provider</option> - <option value="g4f.Provider.AItianhuSpace">AItianhuSpace</option> - <option value="g4f.Provider.ChatgptLogin">ChatgptLogin</option> - <option value="g4f.Provider.ChatgptDemo">ChatgptDemo</option> - <option value="g4f.Provider.ChatgptDuo">ChatgptDuo</option> - <option value="g4f.Provider.Vitalentum">Vitalentum</option> - <option value="g4f.Provider.ChatgptAi">ChatgptAi</option> - <option value="g4f.Provider.AItianhu">AItianhu</option> - <option value="g4f.Provider.ChatBase">ChatBase</option> - <option value="g4f.Provider.Liaobots">Liaobots</option> - <option value="g4f.Provider.Yqcloud">Yqcloud</option> - <option value="g4f.Provider.Myshell">Myshell</option> - <option value="g4f.Provider.FreeGpt">FreeGpt</option> - <option value="g4f.Provider.Vercel">Vercel</option> - <option value="g4f.Provider.Aichat">Aichat</option> - <option value="g4f.Provider.GPTalk">GPTalk</option> - <option value="g4f.Provider.GptGod">GptGod</option> - <option value="g4f.Provider.AiAsk">AiAsk</option> - <option value="g4f.Provider.GptGo">GptGo</option> - <option value="g4f.Provider.Ylokh">Ylokh</option> - <option value="g4f.Provider.Bard">Bard</option> - <option value="g4f.Provider.Aibn">Aibn</option> - <option value="g4f.Provider.Bing">Bing</option> - <option value="g4f.Provider.You">You</option> - <option value="g4f.Provider.Llama2">Llama2</option> - <option value="g4f.Provider.Aivvm">Aivvm</option> - </select> + <select name="provider" id="provider"></select> </div> </div> + <div class="field"> + <input type="checkbox" id="switch" /> + <label for="switch"></label> + <span class="about">Web Access</span> + </div> + <div class="field"> + <input type="checkbox" id="patch" /> + <label for="patch" title="Works only with Bing and some other providers"></label> + <span class="about">Image Generator</span> + </div> </div> </div> </div> diff --git a/g4f/gui/client/js/chat.v1.js b/g4f/gui/client/js/chat.v1.js index fffe9fe9..e763f52d 100644 --- a/g4f/gui/client/js/chat.v1.js +++ b/g4f/gui/client/js/chat.v1.js @@ -7,6 +7,7 @@ const spinner = box_conversations.querySelector(".spinner"); const stop_generating = document.querySelector(`.stop_generating`); const regenerate = document.querySelector(`.regenerate`); const send_button = document.querySelector(`#send-button`); +const imageInput = document.querySelector('#image') ; let prompt_lock = false; hljs.addPlugin(new CopyButtonPlugin()); @@ -34,7 +35,7 @@ const delete_conversations = async () => { }; const handle_ask = async () => { - message_input.style.height = `80px`; + message_input.style.height = `82px`; message_input.focus(); window.scrollTo(0, 0); message = message_input.value @@ -103,8 +104,7 @@ const ask_gpt = async () => { </div> <div class="content" id="gpt_${window.token}"> <div class="provider"></div> - <div class="content_inner"></div> - <div id="cursor"></div> + <div class="content_inner"><div id="cursor"></div></div> </div> </div> `; @@ -114,29 +114,32 @@ const ask_gpt = async () => { message_box.scrollTop = message_box.scrollHeight; window.scrollTo(0, 0); try { + let body = JSON.stringify({ + id: window.token, + conversation_id: window.conversation_id, + model: model.options[model.selectedIndex].value, + jailbreak: jailbreak.options[jailbreak.selectedIndex].value, + web_search: document.getElementById(`switch`).checked, + provider: provider.options[provider.selectedIndex].value, + patch_provider: document.getElementById('patch').checked, + messages: messages + }); + const headers = { + accept: 'text/event-stream' + } + if (imageInput && imageInput.files.length > 0) { + const formData = new FormData(); + formData.append('image', imageInput.files[0]); + formData.append('json', body); + body = formData; + } else { + headers['content-type'] = 'application/json'; + } const response = await fetch(`/backend-api/v2/conversation`, { - method: `POST`, + method: 'POST', signal: window.controller.signal, - headers: { - 'content-type': `application/json`, - accept: `text/event-stream`, - }, - body: JSON.stringify({ - conversation_id: window.conversation_id, - action: `_ask`, - model: model.options[model.selectedIndex].value, - jailbreak: jailbreak.options[jailbreak.selectedIndex].value, - internet_access: document.getElementById(`switch`).checked, - provider: provider.options[provider.selectedIndex].value, - patch_provider: document.getElementById('patch').checked, - meta: { - id: window.token, - content: { - content_type: `text`, - parts: messages, - }, - }, - }), + headers: headers, + body: body }); await new Promise((r) => setTimeout(r, 1000)); @@ -159,13 +162,17 @@ const ask_gpt = async () => { '<a href="' + provider.url + '" target="_blank">' + provider.name + "</a>" } else if (message["type"] == "error") { error = message["error"]; + } else if (message["type"] == "message") { + console.error(message["message"]) } } if (error) { console.error(error); content_inner.innerHTML = "An error occured, please try again, if the problem persists, please use a other model or provider"; } else { - content_inner.innerHTML = markdown_render(text); + html = markdown_render(text); + html = html.substring(0, html.lastIndexOf('</p>')) + '<span id="cursor"></span></p>'; + content_inner.innerHTML = html; document.querySelectorAll('code').forEach((el) => { hljs.highlightElement(el); }); @@ -174,9 +181,9 @@ const ask_gpt = async () => { window.scrollTo(0, 0); message_box.scrollTo({ top: message_box.scrollHeight, behavior: "auto" }); } + if (!error && imageInput) imageInput.value = ""; } catch (e) { - console.log(e); - + console.error(e); if (e.name != `AbortError`) { text = `oops ! something went wrong, please try again / reload. [stacktrace in console]`; @@ -444,34 +451,34 @@ document.querySelector(".mobile-sidebar").addEventListener("click", (event) => { }); const register_settings_localstorage = async () => { - settings_ids = ["switch", "model", "jailbreak", "patch", "provider"]; - settings_elements = settings_ids.map((id) => document.getElementById(id)); - settings_elements.map((element) => - element.addEventListener(`change`, async (event) => { + for (id of ["switch", "model", "jailbreak", "patch", "provider"]) { + element = document.getElementById(id); + element.addEventListener('change', async (event) => { switch (event.target.type) { case "checkbox": - localStorage.setItem(event.target.id, event.target.checked); + localStorage.setItem(id, event.target.checked); break; case "select-one": - localStorage.setItem(event.target.id, event.target.selectedIndex); + localStorage.setItem(id, event.target.selectedIndex); break; default: console.warn("Unresolved element type"); } - }) - ); -}; + }); + } +} const load_settings_localstorage = async () => { for (id of ["switch", "model", "jailbreak", "patch", "provider"]) { element = document.getElementById(id); - if (localStorage.getItem(element.id)) { + value = localStorage.getItem(element.id); + if (value) { switch (element.type) { case "checkbox": - element.checked = localStorage.getItem(element.id) === "true"; + element.checked = value === "true"; break; case "select-one": - element.selectedIndex = parseInt(localStorage.getItem(element.id)); + element.selectedIndex = parseInt(value); break; default: console.warn("Unresolved element type"); @@ -529,7 +536,6 @@ colorThemes.forEach((themeOption) => { window.onload = async () => { - load_settings_localstorage(); setTheme(); let conversations = 0; @@ -610,16 +616,14 @@ observer.observe(message_input, { attributes: true }); option.value = option.text = model; select.appendChild(option); } -})(); -(async () => { response = await fetch('/backend-api/v2/providers') providers = await response.json() - let select = document.getElementById('provider'); + select = document.getElementById('provider'); select.textContent = ''; - let auto = document.createElement('option'); + auto = document.createElement('option'); auto.value = ''; auto.text = 'Provider: Auto'; select.appendChild(auto); @@ -629,6 +633,8 @@ observer.observe(message_input, { attributes: true }); option.value = option.text = provider; select.appendChild(option); } + + await load_settings_localstorage() })(); (async () => { @@ -644,4 +650,4 @@ observer.observe(message_input, { attributes: true }); text += versions["version"]; } document.getElementById("version_text").innerHTML = text -})();
\ No newline at end of file +})()
\ No newline at end of file diff --git a/g4f/gui/server/backend.py b/g4f/gui/server/backend.py index 67f13de4..3ccd1a59 100644 --- a/g4f/gui/server/backend.py +++ b/g4f/gui/server/backend.py @@ -3,6 +3,7 @@ import json from flask import request, Flask from g4f import debug, version, models from g4f import _all_models, get_last_provider, ChatCompletion +from g4f.image import is_allowed_extension, to_image from g4f.Provider import __providers__ from g4f.Provider.bing.create_images import patch_provider from .internet import get_search_message @@ -55,7 +56,7 @@ class Backend_Api: def version(self): return { "version": version.utils.current_version, - "lastet_version": version.utils.latest_version, + "lastet_version": version.get_latest_version(), } def _gen_title(self): @@ -64,15 +65,31 @@ class Backend_Api: } def _conversation(self): - #jailbreak = request.json['jailbreak'] - messages = request.json['meta']['content']['parts'] - if request.json.get('internet_access'): - messages[-1]["content"] = get_search_message(messages[-1]["content"]) - model = request.json.get('model') + kwargs = {} + if 'image' in request.files: + file = request.files['image'] + if file.filename != '' and is_allowed_extension(file.filename): + kwargs['image'] = to_image(file.stream) + if 'json' in request.form: + json_data = json.loads(request.form['json']) + else: + json_data = request.json + + provider = json_data.get('provider', '').replace('g4f.Provider.', '') + provider = provider if provider and provider != "Auto" else None + if provider == 'OpenaiChat': + kwargs['auto_continue'] = True + messages = json_data['messages'] + if json_data.get('web_search'): + if provider == "Bing": + kwargs['web_search'] = True + else: + messages[-1]["content"] = get_search_message(messages[-1]["content"]) + model = json_data.get('model') model = model if model else models.default - provider = request.json.get('provider', '').replace('g4f.Provider.', '') + provider = json_data.get('provider', '').replace('g4f.Provider.', '') provider = provider if provider and provider != "Auto" else None - patch = patch_provider if request.json.get('patch_provider') else None + patch = patch_provider if json_data.get('patch_provider') else None def try_response(): try: @@ -83,7 +100,8 @@ class Backend_Api: messages=messages, stream=True, ignore_stream_and_auth=True, - patch_provider=patch + patch_provider=patch, + **kwargs ): if first: first = False @@ -91,16 +109,24 @@ class Backend_Api: 'type' : 'provider', 'provider': get_last_provider(True) }) + "\n" - yield json.dumps({ - 'type' : 'content', - 'content': chunk, - }) + "\n" - + if isinstance(chunk, Exception): + yield json.dumps({ + 'type' : 'message', + 'message': get_error_message(chunk), + }) + "\n" + else: + yield json.dumps({ + 'type' : 'content', + 'content': str(chunk), + }) + "\n" except Exception as e: logging.exception(e) yield json.dumps({ 'type' : 'error', - 'error': f'{e.__class__.__name__}: {e}' + 'error': get_error_message(e) }) - return self.app.response_class(try_response(), mimetype='text/event-stream')
\ No newline at end of file + return self.app.response_class(try_response(), mimetype='text/event-stream') + +def get_error_message(exception: Exception) -> str: + return f"{get_last_provider().__name__}: {type(exception).__name__}: {exception}"
\ No newline at end of file |