summaryrefslogtreecommitdiffstats
path: root/g4f/gui
diff options
context:
space:
mode:
authorHeiner Lohaus <hlohaus@users.noreply.github.com>2024-04-08 07:24:00 +0200
committerHeiner Lohaus <hlohaus@users.noreply.github.com>2024-04-08 07:24:00 +0200
commit926ddfd5438ca06840bdff9b9bd21ddcd8863aa9 (patch)
treeb85cf6df7365e5800d9417ce14b010dc0142d727 /g4f/gui
parentAdd nodriver to OpenaiChat (diff)
downloadgpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar.gz
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar.bz2
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar.lz
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar.xz
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.tar.zst
gpt4free-926ddfd5438ca06840bdff9b9bd21ddcd8863aa9.zip
Diffstat (limited to '')
-rw-r--r--g4f/gui/client/index.html101
-rw-r--r--g4f/gui/client/static/css/style.css57
-rw-r--r--g4f/gui/client/static/js/chat.v1.js102
-rw-r--r--g4f/gui/server/api.py13
-rw-r--r--g4f/gui/server/js_api.py1
-rw-r--r--g4f/gui/server/website.py12
6 files changed, 192 insertions, 94 deletions
diff --git a/g4f/gui/client/index.html b/g4f/gui/client/index.html
index 7103b9c3..31107a6b 100644
--- a/g4f/gui/client/index.html
+++ b/g4f/gui/client/index.html
@@ -57,13 +57,9 @@
</button>
</div>
<div class="bottom_buttons">
- <button onclick="delete_conversations()">
- <i class="fa-regular fa-trash"></i>
- <span>Clear Conversations</span>
- </button>
- <button onclick="save_storage()">
- <i class="fa-solid fa-download"></i>
- <a href="" onclick="return false;">Export Conversations</a>
+ <button onclick="open_settings();">
+ <i class="fa-solid fa-toolbox"></i>
+ <span>Open Settings</span>
</button>
<div class="info">
<i class="fa-brands fa-github"></i>
@@ -76,22 +72,38 @@
</div>
</div>
</div>
- <div class="settings">
- <div class="field box">
- <label for="OpenaiChat-api_key" class="label" title="">OpenaiChat: access_token</label>
- <textarea id="OpenaiChat-api_key" name="OpenaiChat[api_key]" placeholder="..."></textarea>
+ <div class="settings hidden">
+ <div class="field">
+ <span class="label">Web Access</span>
+ <input type="checkbox" id="switch" />
+ <label for="switch" class="toogle" title="Add the pages of the first 5 search results to the query."></label>
</div>
<div class="field">
- <span class="label">OpenaiChat: Auto continue</span>
- <input id="OpenaiChat-auto_continue" type="checkbox" name="OpenaiChat[auto_continue]" checked/>
- <label for="OpenaiChat-auto_continue" class="toogle" title=""></label>
+ <span class="label">Disable History</span>
+ <input type="checkbox" id="history" />
+ <label for="history" class="toogle" title="To improve the reaction time or if you have trouble with large conversations."></label>
+ </div>
+ <div class="field">
+ <span class="label">Hide System prompt</span>
+ <input type="checkbox" id="hide-systemPrompt" />
+ <label for="hide-systemPrompt" class="toogle" title="For more space on phones"></label>
+ </div>
+ <div class="field">
+ <span class="label">Auto continue</span>
+ <input id="auto_continue" type="checkbox" name="auto_continue" checked/>
+ <label for="auto_continue" class="toogle" title="Continue long responses in OpenaiChat"></label>
+ </div>
+ <div class="paper">
+ <div class="field box">
+ <label for="OpenaiChat-api_key" class="label" title="">OpenaiChat: api_key</label>
+ <textarea id="OpenaiChat-api_key" name="OpenaiChat[api_key]" placeholder="..."></textarea>
</div>
<div class="field box">
<label for="Bing-api_key" class="label" title="">Bing: "_U" cookie</label>
<textarea id="Bing-api_key" name="Bing[api_key]" placeholder="..."></textarea>
</div>
<div class="field box">
- <label for="Gemini-api_key" class="label" title="">Gemini: Auth cookies</label>
+ <label for="Gemini-api_key" class="label" title="">Gemini: Cookies</label>
<textarea id="Gemini-api_key" name="Gemini[api_key]" placeholder="..."></textarea>
</div>
<div class="field box">
@@ -99,6 +111,10 @@
<textarea id="Openai-api_key" name="Openai[api_key]" placeholder="..."></textarea>
</div>
<div class="field box">
+ <label for="Openai-api_key" class="label" title="">Groq: api_key</label>
+ <textarea id="Openai-api_key" name="Groq[api_key]" placeholder="..."></textarea>
+ </div>
+ <div class="field box">
<label for="GeminiPro-api_key" class="label" title="">GeminiPro: api_key</label>
<textarea id="GeminiPro-api_key" name="GeminiPro[api_key]" placeholder="..."></textarea>
</div>
@@ -106,6 +122,17 @@
<label for="HuggingFace-api_key" class="label" title="">HuggingFace: api_key</label>
<textarea id="HuggingFace-api_key" name="HuggingFace[api_key]" placeholder="..."></textarea>
</div>
+ </div>
+ <div class="bottom_buttons">
+ <button onclick="delete_conversations()">
+ <i class="fa-regular fa-trash"></i>
+ <span>Clear Conversations</span>
+ </button>
+ <button onclick="save_storage()">
+ <i class="fa-solid fa-download"></i>
+ <a href="" onclick="return false;">Export Conversations</a>
+ </button>
+ </div>
</div>
<div class="conversation">
<textarea id="systemPrompt" class="box" placeholder="System prompt"></textarea>
@@ -161,43 +188,15 @@
<select name="model2" id="model2" class="hidden"></select>
</div>
<div class="field">
- <select name="jailbreak" id="jailbreak" style="display: none;">
- <option value="default" selected>Set Jailbreak</option>
- <option value="gpt-math-1.0">math 1.0</option>
- <option value="gpt-dude-1.0">dude 1.0</option>
- <option value="gpt-dan-1.0">dan 1.0</option>
- <option value="gpt-dan-2.0">dan 2.0</option>
- <option value="gpt-dev-2.0">dev 2.0</option>
- <option value="gpt-evil-1.0">evil 1.0</option>
+ <select name="provider" id="provider">
+ <option value="">Provider: Auto</option>
+ <option value="Bing">Bing</option>
+ <option value="OpenaiChat">OpenaiChat</option>
+ <option value="Gemini">Gemini</option>
+ <option value="Liaobots">Liaobots</option>
+ <option value="You">You</option>
+ <option value="">----</option>
</select>
- <div class="field">
- <select name="provider" id="provider">
- <option value="">Provider: Auto</option>
- <option value="Bing">Bing</option>
- <option value="OpenaiChat">OpenaiChat</option>
- <option value="Gemini">Gemini</option>
- <option value="Liaobots">Liaobots</option>
- <option value="You">You</option>
- <option value="">----</option>
- </select>
- </div>
- </div>
- <div class="field">
- <input type="checkbox" id="switch" />
- <label for="switch" title="Add the pages of the first 5 search results to the query."></label>
- <span class="about">Web Access</span>
- </div>
- <!--
- <div class="field">
- <input type="checkbox" id="patch" />
- <label for="patch" title="Enable create images with Bing."></label>
- <span class="about">Image Generator</span>
- </div>
- -->
- <div class="field">
- <input type="checkbox" id="history" />
- <label for="history" title="To improve the reaction time or if you have trouble with large conversations."></label>
- <span class="about">Disable History</span>
</div>
</div>
</div>
diff --git a/g4f/gui/client/static/css/style.css b/g4f/gui/client/static/css/style.css
index 4db65863..9e02a3ec 100644
--- a/g4f/gui/client/static/css/style.css
+++ b/g4f/gui/client/static/css/style.css
@@ -84,7 +84,7 @@ body {
}
body {
- padding: var(--section-gap);
+ padding: 10px;
background: var(--colour-1);
color: var(--colour-3);
height: 100vh;
@@ -92,7 +92,7 @@ body {
.row {
display: flex;
- gap: var(--section-gap);
+ gap: 10px;
height: 100%;
}
@@ -111,7 +111,7 @@ body {
}
.conversations {
- max-width: 260px;
+ max-width: 280px;
padding: var(--section-gap);
overflow: auto;
flex-shrink: 0;
@@ -183,8 +183,8 @@ body {
.conversations {
display: flex;
flex-direction: column;
- gap: var(--inner-gap);
- padding: var(--inner-gap);
+ gap: 10px;
+ padding: 10px;
}
.conversations .title {
@@ -207,7 +207,12 @@ body {
cursor: pointer;
display: flex;
align-items: center;
- gap: 10px;
+ gap: 4px;
+}
+
+.conversations .convo .fa-trash {
+ position: absolute;
+ right: 8px;
}
.conversations .convo .choise {
@@ -216,7 +221,7 @@ body {
background-color: var(--blur-bg);
}
-.conversations i {
+.conversations i, .bottom_buttons i {
color: var(--conversations);
cursor: pointer;
}
@@ -229,6 +234,10 @@ body {
white-space: nowrap;
}
+.convo-title .datetime {
+ font-size: 10px;
+}
+
.message {
width: 100%;
overflow-wrap: break-word;
@@ -351,10 +360,23 @@ body {
cursor: pointer;
}
+.message .count .fa-clipboard {
+ z-index: 1000;
+ cursor: pointer;
+}
+
.message .user .fa-xmark {
color: var(--colour-1);
}
+.message .count .fa-clipboard {
+ color: var(--colour-3);
+}
+
+.message .count .fa-clipboard.clicked {
+ color: var(--accent);
+}
+
.message .assistant:hover .fa-xmark,
.message .user:hover .fa-xmark {
display: block;
@@ -556,6 +578,16 @@ label[for="camera"] {
background: var(--accent);
}
+.settings .bottom_buttons {
+ flex-direction: row;
+}
+
+.settings .bottom_buttons button {
+ display: inline-block;
+ max-width: 210px;
+ width: 100%;
+}
+
.buttons input:checked+label:after {
left: calc(100% - 5px - 20px);
}
@@ -565,6 +597,7 @@ label[for="camera"] {
align-items: center;
justify-content: left;
width: 100%;
+ margin-bottom: 2px;
}
.field {
@@ -635,6 +668,7 @@ select {
display: flex;
flex-direction: column;
gap: 10px;
+ margin: 4px;
}
.bottom_buttons button {
@@ -1026,7 +1060,14 @@ a:-webkit-any-link {
.settings {
width: 100%;
- display: none;
+ min-width: 700px;
+ display: flex;
+ flex-direction: column;
+}
+
+.settings .paper {
+ overflow: auto;
+ flex-direction: column;
}
.settings .field {
diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js
index a3a1cccf..e0ba020f 100644
--- a/g4f/gui/client/static/js/chat.v1.js
+++ b/g4f/gui/client/static/js/chat.v1.js
@@ -15,13 +15,13 @@ const providerSelect = document.getElementById("provider");
const modelSelect = document.getElementById("model");
const modelProvider = document.getElementById("model2");
const systemPrompt = document.getElementById("systemPrompt")
-const jailbreak = document.getElementById("jailbreak");
+const settings = document.querySelector(".settings")
let prompt_lock = false;
let content, content_inner, content_count = null;
-const options = ["switch", "model", "model2", "jailbreak", "patch", "provider", "history"];
+const optionElements = document.querySelectorAll(".settings input, .settings textarea, #model, #model2, #provider")
messageInput.addEventListener("blur", () => {
window.scrollTo(0, 0);
@@ -63,7 +63,7 @@ const highlight = (container) => {
);
}
-const register_remove_message = async () => {
+const register_message_buttons = async () => {
document.querySelectorAll(".message .fa-xmark").forEach(async (el) => {
if (!("click" in el.dataset)) {
el.dataset.click = "true";
@@ -77,6 +77,18 @@ const register_remove_message = async () => {
})
}
});
+ document.querySelectorAll(".message .fa-clipboard").forEach(async (el) => {
+ if (!("click" in el.dataset)) {
+ el.dataset.click = "true";
+ el.addEventListener("click", async () => {
+ const message_el = el.parentElement.parentElement;
+ const copyText = await get_message(window.conversation_id, message_el.dataset.index);
+ navigator.clipboard.writeText(copyText);
+ el.classList.add("clicked");
+ setTimeout(() => el.classList.remove("clicked"), 1000);
+ })
+ }
+ });
}
const delete_conversations = async () => {
@@ -132,7 +144,7 @@ const handle_ask = async () => {
: ''
}
</div>
- <div class="count">${count_words_and_tokens(message, get_selected_model())}</div>
+ <div class="count">${count_words_and_tokens(message, get_selected_model())} <i class="fa-regular fa-clipboard"></i></div>
</div>
</div>
`;
@@ -305,15 +317,22 @@ const ask_gpt = async () => {
try {
const input = imageInput && imageInput.files.length > 0 ? imageInput : cameraInput;
const file = input && input.files.length > 0 ? input.files[0] : null;
+ const provider = providerSelect.options[providerSelect.selectedIndex].value;
+ const auto_continue = document.getElementById("auto_continue")?.checked;
+ if (file && !provider)
+ provider = "Bing";
+ let api_key = null;
+ if (provider)
+ api_key = document.getElementById(`${provider}-api_key`)?.value;
await api("conversation", {
id: window.token,
conversation_id: window.conversation_id,
model: get_selected_model(),
- jailbreak: jailbreak?.options[jailbreak.selectedIndex].value,
web_search: document.getElementById("switch").checked,
- provider: providerSelect.options[providerSelect.selectedIndex].value,
- patch_provider: document.getElementById("patch")?.checked,
- messages: messages
+ provider: provider,
+ messages: messages,
+ auto_continue: auto_continue,
+ api_key: api_key
}, file);
if (!error) {
html = markdown_render(text);
@@ -341,7 +360,7 @@ const ask_gpt = async () => {
window.scrollTo(0, 0);
message_box.scrollTop = message_box.scrollHeight;
await remove_cancel_button();
- await register_remove_message();
+ await register_message_buttons();
prompt_lock = false;
await load_conversations();
regenerate.classList.remove("regenerate-hidden");
@@ -459,7 +478,7 @@ const load_conversation = async (conversation_id, scroll=true) => {
<div class="content">
${provider}
<div class="content_inner">${markdown_render(item.content)}</div>
- <div class="count">${count_words_and_tokens(item.content, next_provider?.model)}</div>
+ <div class="count">${count_words_and_tokens(item.content, next_provider?.model)} <i class="fa-regular fa-clipboard"></i></div>
</div>
</div>
`;
@@ -475,8 +494,9 @@ const load_conversation = async (conversation_id, scroll=true) => {
}
message_box.innerHTML = elements;
- register_remove_message();
+ register_message_buttons();
highlight(message_box);
+ regenerate.classList.remove("regenerate-hidden");
if (scroll) {
message_box.scrollTo({ top: message_box.scrollHeight, behavior: "smooth" });
@@ -495,6 +515,7 @@ async function get_conversation(conversation_id) {
}
async function save_conversation(conversation_id, conversation) {
+ conversation.updated = Date.now();
appStorage.setItem(
`conversation:${conversation_id}`,
JSON.stringify(conversation)
@@ -517,6 +538,7 @@ async function add_conversation(conversation_id, content) {
await save_conversation(conversation_id, {
id: conversation_id,
title: title,
+ added: Date.now(),
system: systemPrompt?.value,
items: [],
});
@@ -563,6 +585,11 @@ const remove_message = async (conversation_id, index) => {
await save_conversation(conversation_id, conversation);
};
+const get_message = async (conversation_id, index) => {
+ const conversation = await get_conversation(conversation_id);
+ return conversation.items[index]["content"];
+};
+
const add_message = async (conversation_id, role, content, provider) => {
const conversation = await get_conversation(conversation_id);
conversation.items.push({
@@ -586,11 +613,17 @@ const load_conversations = async () => {
await clear_conversations();
for (conversation of conversations) {
+ let updated = "";
+ if (conversation.updated) {
+ const date = new Date(conversation.updated);
+ updated = date.toLocaleString('en-GB', {dateStyle: 'short', timeStyle: 'short', monthStyle: 'short'});
+ updated = updated.replace("/" + date.getFullYear(), "")
+ }
box_conversations.innerHTML += `
<div class="convo" id="convo-${conversation.id}">
<div class="left" onclick="set_conversation('${conversation.id}')">
<i class="fa-regular fa-comments"></i>
- <span class="convo-title">${conversation.title}</span>
+ <span class="convo-title"><span class="datetime">${updated}</span> ${conversation.title}</span>
</div>
<i onclick="show_option('${conversation.id}')" class="fa-regular fa-trash" id="conv-${conversation.id}"></i>
<div id="cho-${conversation.id}" class="choise" style="display:none;">
@@ -642,7 +675,8 @@ const message_id = () => {
async function hide_sidebar() {
sidebar.classList.remove("shown");
sidebar_button.classList.remove("rotated");
- if (window.location.pathname == "/menu/") {
+ settings.classList.add("hidden");
+ if (window.location.pathname == "/menu/" || window.location.pathname == "/settings/") {
history.back();
}
}
@@ -650,6 +684,7 @@ async function hide_sidebar() {
window.addEventListener('popstate', hide_sidebar, false);
sidebar_button.addEventListener("click", (event) => {
+ settings.classList.add("hidden");
if (sidebar.classList.contains("shown")) {
hide_sidebar();
} else {
@@ -660,19 +695,25 @@ sidebar_button.addEventListener("click", (event) => {
window.scrollTo(0, 0);
});
+function open_settings() {
+ if (settings.classList.contains("hidden")) {
+ sidebar.classList.remove("shown");
+ settings.classList.remove("hidden");
+ history.pushState({}, null, "/settings/");
+ } else {
+ settings.classList.add("hidden");
+ }
+}
+
const register_settings_storage = async () => {
- options.forEach((id) => {
- element = document.getElementById(id);
- if (!element) {
- return;
- }
+ optionElements.forEach((element) => {
element.addEventListener('change', async (event) => {
switch (event.target.type) {
case "checkbox":
- appStorage.setItem(id, event.target.checked);
+ appStorage.setItem(element.id, event.target.checked);
break;
case "select-one":
- appStorage.setItem(id, event.target.selectedIndex);
+ appStorage.setItem(element.id, event.target.selectedIndex);
break;
default:
console.warn("Unresolved element type");
@@ -682,9 +723,8 @@ const register_settings_storage = async () => {
}
const load_settings_storage = async () => {
- options.forEach((id) => {
- element = document.getElementById(id);
- if (!element || !(value = appStorage.getItem(id))) {
+ optionElements.forEach((element) => {
+ if (!(value = appStorage.getItem(element.id))) {
return;
}
if (value) {
@@ -859,6 +899,18 @@ async function on_api() {
await load_provider_models(appStorage.getItem("provider"));
await load_settings_storage()
+
+ const hide_systemPrompt = document.getElementById("hide-systemPrompt")
+ if (hide_systemPrompt.checked) {
+ systemPrompt.classList.add("hidden");
+ }
+ hide_systemPrompt.addEventListener('change', async (event) => {
+ if (event.target.checked) {
+ systemPrompt.classList.add("hidden");
+ } else {
+ systemPrompt.classList.remove("hidden");
+ }
+ });
}
async function load_version() {
@@ -875,7 +927,7 @@ async function load_version() {
}
document.getElementById("version_text").innerHTML = text
}
-setTimeout(load_version, 5000);
+setTimeout(load_version, 2000);
for (const el of [imageInput, cameraInput]) {
el.addEventListener('click', async () => {
@@ -1035,7 +1087,7 @@ function save_storage() {
let item = appStorage.getItem(key);
if (key.startsWith("conversation:")) {
data[key] = JSON.parse(item);
- } else {
+ } else if (!key.includes("api_key")) {
data["options"][key] = item;
}
}
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index 7c09fdbe..2b3f2fb6 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -80,13 +80,12 @@ class Api():
Returns:
dict: Arguments prepared for chat completion.
"""
- provider = json_data.get('provider', None)
- if "image" in kwargs and provider is None:
- provider = "Bing"
- if provider == 'OpenaiChat':
- kwargs['auto_continue'] = True
-
+ model = json_data.get('model') or models.default
+ provider = json_data.get('provider')
messages = json_data['messages']
+ api_key = json_data.get("api_key")
+ if api_key is not None:
+ kwargs["api_key"] = api_key
if json_data.get('web_search'):
if provider == "Bing":
kwargs['web_search'] = True
@@ -98,8 +97,6 @@ class Api():
if conversation_id and provider in conversations and conversation_id in conversations[provider]:
kwargs["conversation"] = conversations[provider][conversation_id]
- model = json_data.get('model') or models.default
-
return {
"model": model,
"provider": provider,
diff --git a/g4f/gui/server/js_api.py b/g4f/gui/server/js_api.py
index e5550f27..08eed947 100644
--- a/g4f/gui/server/js_api.py
+++ b/g4f/gui/server/js_api.py
@@ -1,6 +1,5 @@
from __future__ import annotations
-import logging
import json
import os.path
from typing import Iterator
diff --git a/g4f/gui/server/website.py b/g4f/gui/server/website.py
index 4e611177..5e633674 100644
--- a/g4f/gui/server/website.py
+++ b/g4f/gui/server/website.py
@@ -4,9 +4,11 @@ from flask import render_template, redirect
class Website:
def __init__(self, app) -> None:
self.app = app
+ def redirect_home():
+ return redirect('/chat')
self.routes = {
'/': {
- 'function': lambda: redirect('/chat'),
+ 'function': redirect_home,
'methods': ['GET', 'POST']
},
'/chat/': {
@@ -17,6 +19,14 @@ class Website:
'function': self._chat,
'methods': ['GET', 'POST']
},
+ '/menu/': {
+ 'function': redirect_home,
+ 'methods': ['GET', 'POST']
+ },
+ '/settings/': {
+ 'function': redirect_home,
+ 'methods': ['GET', 'POST']
+ },
}
def _chat(self, conversation_id):