From 926ddfd5438ca06840bdff9b9bd21ddcd8863aa9 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Mon, 8 Apr 2024 07:24:00 +0200 Subject: [PATCH 1/5] Add WhiteRabbitNeo Provider, Many tiny improvments in the gui --- .github/FUNDING.yml | 2 +- g4f/Provider/HuggingFace.py | 6 +- g4f/Provider/WhiteRabbitNeo.py | 57 ++++++++++++++++ g4f/Provider/__init__.py | 1 + g4f/gui/client/index.html | 101 ++++++++++++++------------- g4f/gui/client/static/css/style.css | 57 +++++++++++++--- g4f/gui/client/static/js/chat.v1.js | 102 +++++++++++++++++++++------- g4f/gui/server/api.py | 13 ++-- g4f/gui/server/js_api.py | 1 - g4f/gui/server/website.py | 12 +++- 10 files changed, 256 insertions(+), 96 deletions(-) create mode 100644 g4f/Provider/WhiteRabbitNeo.py diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e0b12bae..98f1e5b0 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ ko_fi: xtekky -github: [xtekky] +github: [xtekky, hlohaus] patreon: xtekky diff --git a/g4f/Provider/HuggingFace.py b/g4f/Provider/HuggingFace.py index 647780fd..6a05c26e 100644 --- a/g4f/Provider/HuggingFace.py +++ b/g4f/Provider/HuggingFace.py @@ -13,6 +13,10 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin): url = "https://huggingface.co/chat" working = True supports_message_history = True + models = [ + "mistralai/Mixtral-8x7B-Instruct-v0.1", + "mistralai/Mistral-7B-Instruct-v0.2" + ] default_model = "mistralai/Mixtral-8x7B-Instruct-v0.1" @classmethod @@ -29,7 +33,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin): temperature: float = 0.7, **kwargs ) -> AsyncResult: - model = cls.get_model(model) + model = cls.get_model(model) if not model else model headers = {} if api_key is not None: headers["Authorization"] = f"Bearer {api_key}" diff --git a/g4f/Provider/WhiteRabbitNeo.py b/g4f/Provider/WhiteRabbitNeo.py new file mode 100644 index 00000000..339434e6 --- /dev/null +++ b/g4f/Provider/WhiteRabbitNeo.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from aiohttp import ClientSession, BaseConnector + +from ..typing import AsyncResult, Messages, Cookies +from ..requests.raise_for_status import raise_for_status +from .base_provider import AsyncGeneratorProvider +from .helper import get_cookies, get_connector, get_random_string + +class WhiteRabbitNeo(AsyncGeneratorProvider): + url = "https://www.whiterabbitneo.com" + working = True + supports_message_history = True + needs_auth = True + + @classmethod + async def create_async_generator( + cls, + model: str, + messages: Messages, + cookies: Cookies = None, + connector: BaseConnector = None, + proxy: str = None, + **kwargs + ) -> AsyncResult: + if cookies is None: + cookies = get_cookies("www.whiterabbitneo.com") + headers = { + "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0", + "Accept": "*/*", + "Accept-Language": "de,en-US;q=0.7,en;q=0.3", + "Accept-Encoding": "gzip, deflate, br", + "Referer": f"{cls.url}/", + "Content-Type": "text/plain;charset=UTF-8", + "Origin": cls.url, + "Connection": "keep-alive", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + "TE": "trailers" + } + async with ClientSession( + headers=headers, + cookies=cookies, + connector=get_connector(connector, proxy) + ) as session: + data = { + "messages": messages, + "id": get_random_string(6), + "enhancePrompt": False, + "useFunctions": False + } + async with session.post(f"{cls.url}/api/chat", json=data, proxy=proxy) as response: + await raise_for_status(response) + async for chunk in response.content.iter_any(): + if chunk: + yield chunk.decode(errors="ignore") \ No newline at end of file diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py index 1db29e19..2c6512f0 100644 --- a/g4f/Provider/__init__.py +++ b/g4f/Provider/__init__.py @@ -37,6 +37,7 @@ from .Local import Local from .PerplexityLabs import PerplexityLabs from .Pi import Pi from .Vercel import Vercel +from .WhiteRabbitNeo import WhiteRabbitNeo from .You import You import sys 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 @@
- -
@@ -76,28 +72,48 @@
-
-
- - +
@@ -161,43 +188,15 @@
- + + + + + + + -
- -
-
-
- - - Web Access -
- -
- - - Disable History
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 () => { : '' } -
${count_words_and_tokens(message, get_selected_model())}
+
${count_words_and_tokens(message, get_selected_model())}
`; @@ -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) => {
${provider}
${markdown_render(item.content)}
-
${count_words_and_tokens(item.content, next_provider?.model)}
+
${count_words_and_tokens(item.content, next_provider?.model)}
`; @@ -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 += `
- ${conversation.title} + ${updated} ${conversation.title}