summaryrefslogtreecommitdiffstats
path: root/g4f/gui
diff options
context:
space:
mode:
authorH Lohaus <hlohaus@users.noreply.github.com>2024-01-13 15:48:38 +0100
committerGitHub <noreply@github.com>2024-01-13 15:48:38 +0100
commit467f2a6fc67e6c5a6cf996da961dc1f68bd7d456 (patch)
tree70d06c98503a235d8e6727a9ea4537fea4330f8c /g4f/gui
parent~ (diff)
parentSupport upload image in gui (diff)
downloadgpt4free-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.css34
-rw-r--r--g4f/gui/client/html/index.html93
-rw-r--r--g4f/gui/client/js/chat.v1.js96
-rw-r--r--g4f/gui/server/backend.py58
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