Merge pull request #1817 from hlohaus/bugfix

Styling in the gui, GenerateSpeech and SpeechRecognition, Issues with You Provider
This commit is contained in:
H Lohaus 2024-04-10 15:55:01 +02:00 committed by GitHub
commit 48a4f13081
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 182 additions and 105 deletions

5
.gitignore vendored
View File

@ -54,4 +54,7 @@ local.py
*.gguf
image.py
.buildozer
hardir
hardir
node_modules
models
projects/windows/g4f

View File

@ -10,8 +10,6 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt
from ..image import ImageResponse, to_bytes, is_accepted_format
from ..requests import StreamSession, FormData, raise_for_status
from ..errors import MissingRequirementsError
from .you.har_file import get_dfp_telemetry_id
class You(AsyncGeneratorProvider, ProviderModelMixin):

View File

@ -201,6 +201,7 @@ body {
}
.conversations .convo .left {
width: 100%;
cursor: pointer;
display: flex;
align-items: center;
@ -226,9 +227,11 @@ body {
.convo-title {
color: var(--colour-3);
font-size: 14px;
max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
margin-right: 10px;
}
.convo-title .datetime {
@ -406,7 +409,7 @@ body {
.count_total {
font-size: 12px;
padding-left: 100px;
padding-left: 25px;
padding-top: 10px;
}
@ -646,6 +649,21 @@ select {
width: 160px;
}
#systemPrompt, .settings textarea {
font-size: 15px;
width: 100%;
color: var(--colour-3);
min-height: 50px;
height: 59px;
outline: none;
padding: var(--inner-gap) var(--section-gap);
resize: vertical;
}
#systemPrompt {
padding-left: 35px;
}
@media only screen and (min-width: 40em) {
select {
width: 200px;
@ -659,6 +677,9 @@ select {
.settings .bottom_buttons {
flex-direction: row;
}
.count_total {
padding-left: 98px;
}
}
.input-box {
@ -836,6 +857,10 @@ ul {
.mobile-sidebar {
display: flex !important;
}
#systemPrompt {
padding-left: 48px;
}
}
.shown {
@ -1064,22 +1089,11 @@ a:-webkit-any-link {
border: 1px solid #e4d4ffc9;
}
#systemPrompt, .settings textarea {
font-size: 15px;
width: 100%;
color: var(--colour-3);
min-height: 50px;
height: 59px;
outline: none;
padding: var(--inner-gap) var(--section-gap);
resize: vertical;
}
.settings textarea {
height: 51px;
}
.settings {
.settings, .images {
width: 100%;
display: flex;
flex-direction: column;

View File

@ -10,13 +10,14 @@ const sendButton = document.getElementById("send-button");
const imageInput = document.getElementById("image");
const cameraInput = document.getElementById("camera");
const fileInput = document.getElementById("file");
const microLabel = document.querySelector(".micro-label")
const inputCount = document.getElementById("input-count")
const microLabel = document.querySelector(".micro-label");
const inputCount = document.getElementById("input-count");
const providerSelect = document.getElementById("provider");
const modelSelect = document.getElementById("model");
const modelProvider = document.getElementById("model2");
const systemPrompt = document.getElementById("systemPrompt")
const settings = document.querySelector(".settings")
const systemPrompt = document.getElementById("systemPrompt");
const settings = document.querySelector(".settings");
const album = document.querySelector(".images");
let prompt_lock = false;
@ -49,6 +50,12 @@ const markdown_render = (content) => {
.replaceAll('<code>', '<code class="language-plaintext">')
}
function filter_message(text) {
return text.replaceAll(
/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, ""
)
}
hljs.addPlugin(new CopyButtonPlugin());
let typesetPromise = Promise.resolve();
const highlight = (container) => {
@ -57,14 +64,15 @@ const highlight = (container) => {
hljs.highlightElement(el);
}
});
typesetPromise = typesetPromise.then(
() => MathJax.typesetPromise([container])
).catch(
(err) => console.log('Typeset failed: ' + err.message)
);
if (window.MathJax) {
typesetPromise = typesetPromise.then(
() => MathJax.typesetPromise([container])
).catch(
(err) => console.log('Typeset failed: ' + err.message)
);
}
}
let stopped = false;
const register_message_buttons = async () => {
document.querySelectorAll(".message .fa-xmark").forEach(async (el) => {
if (!("click" in el.dataset)) {
@ -95,69 +103,76 @@ const register_message_buttons = async () => {
if (!("click" in el.dataset)) {
el.dataset.click = "true";
el.addEventListener("click", async () => {
if ("active" in el.classList || window.doSpeech) {
el.classList.add("blink")
stopped = true;
return;
let playlist = [];
function play_next() {
const next = playlist.shift();
if (next)
next.play();
}
if (stopped) {
if (el.dataset.stopped) {
el.classList.remove("blink")
stopped = false;
delete el.dataset.stopped;
return;
}
if (el.dataset.running) {
el.dataset.stopped = true;
el.classList.add("blink")
playlist = [];
return;
}
el.dataset.running = true;
el.classList.add("blink")
el.classList.add("active")
const message_el = el.parentElement.parentElement.parentElement;
const content_el = el.parentElement.parentElement;
const message_el = content_el.parentElement;
let speechText = await get_message(window.conversation_id, message_el.dataset.index);
speechText = speechText.replaceAll(/([^0-9])\./gm, "$1.;");
speechText = speechText.replaceAll("?", "?;");
speechText = speechText.replaceAll(/\[(.+)\]\(.+\)/gm, "($1)");
speechText = speechText.replaceAll("`", "").replaceAll("#", "")
speechText = speechText.replaceAll(
/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm,
""
)
speechText = speechText.replaceAll(/```[a-z]+/gm, "");
speechText = filter_message(speechText.replaceAll("`", "").replaceAll("#", ""))
const lines = speechText.trim().split(/\n|;/).filter(v => v.trim());
const lines = speechText.trim().split(/\n|\.|;/);
let ended = true;
window.onSpeechResponse = (url) => {
el.classList.remove("blink")
if (!el.dataset.stopped) {
el.classList.remove("blink")
}
if (url) {
var sound = document.createElement('audio');
sound.controls = 'controls';
sound.src = url;
sound.type = 'audio/wav';
sound.onended = function() {
ended = true;
el.dataset.do_play = true;
setTimeout(play_next, 1000);
};
sound.onplay = function() {
ended = false;
delete el.dataset.do_play;
};
var container = document.createElement('div');
container.classList.add("audio");
container.appendChild(sound);
content_el.appendChild(container);
if (ended && !stopped) {
sound.play();
if (!el.dataset.stopped) {
playlist.push(sound);
if (el.dataset.do_play) {
play_next();
}
}
}
if (lines.length < 1 || stopped) {
let line = lines.length > 0 ? lines.shift() : null;
if (line && !el.dataset.stopped) {
handleGenerateSpeech(line);
} else {
el.classList.remove("active");
return;
}
while (lines.length > 0) {
let line = lines.shift();
var reg = new RegExp('^[0-9]$');
if (line && !reg.test(line)) {
return handleGenerateSpeech(line);
}
}
if (!line) {
el.classList.remove("active")
el.classList.remove("blink");
delete el.dataset.running;
}
}
el.dataset.do_play = true;
let line = lines.shift();
return handleGenerateSpeech(line);
handleGenerateSpeech(line);
});
}
});
@ -399,7 +414,7 @@ const ask_gpt = async () => {
provider = "Bing";
let api_key = null;
if (provider)
api_key = document.getElementById(`${provider}-api_key`)?.value;
api_key = document.getElementById(`${provider}-api_key`)?.value || null;
await api("conversation", {
id: window.token,
conversation_id: window.conversation_id,
@ -564,12 +579,14 @@ const load_conversation = async (conversation_id, scroll=true) => {
`;
}
const filtered = prepare_messages(messages, false);
if (filtered.length > 0) {
last_model = last_model?.startsWith("gpt-4") ? "gpt-4" : "gpt-3.5-turbo"
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
if (count_total > 0) {
elements += `<div class="count_total">(${count_total} tokens used)</div>`;
if (window.GPTTokenizer_cl100k_base) {
const filtered = prepare_messages(messages, false);
if (filtered.length > 0) {
last_model = last_model?.startsWith("gpt-4") ? "gpt-4" : "gpt-3.5-turbo"
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
if (count_total > 0) {
elements += `<div class="count_total">(${count_total} tokens used)</div>`;
}
}
}
@ -603,20 +620,15 @@ async function save_conversation(conversation_id, conversation) {
}
async function get_messages(conversation_id) {
let conversation = await get_conversation(conversation_id);
const conversation = await get_conversation(conversation_id);
return conversation?.items || [];
}
async function add_conversation(conversation_id, content) {
if (content.length > 18) {
title = content.substring(0, 18) + '...'
} else {
title = content + '&nbsp;'.repeat(20 - content.length)
}
if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
await save_conversation(conversation_id, {
id: conversation_id,
title: title,
title: "",
added: Date.now(),
system: systemPrompt?.value,
items: [],
@ -690,13 +702,25 @@ const load_conversations = async () => {
conversations.push(JSON.parse(conversation));
}
}
conversations.sort((a, b) => (b.updated||0)-(a.updated||0));
await clear_conversations();
conversations.sort((a, b) => (b.updated||0)-(a.updated||0));
let html = "";
conversations.forEach((conversation) => {
if (conversation?.items.length > 0) {
let old_value = conversation.title;
let new_value = (conversation.items[0]["content"]).trim();
let new_lenght = new_value.indexOf("\n");
new_lenght = new_lenght > 200 || new_lenght < 0 ? 200 : new_lenght;
conversation.title = new_value.substring(0, new_lenght);
if (conversation.title != old_value) {
appStorage.setItem(
`conversation:${conversation.id}`,
JSON.stringify(conversation)
);
}
}
let updated = "";
if (conversation.updated) {
const date = new Date(conversation.updated);
@ -717,7 +741,7 @@ const load_conversations = async () => {
</div>
`;
});
box_conversations.innerHTML = html;
box_conversations.innerHTML += html;
};
document.getElementById("cancelButton").addEventListener("click", async () => {
@ -790,6 +814,17 @@ function open_settings() {
}
}
function open_album() {
if (album.classList.contains("hidden")) {
sidebar.classList.remove("shown");
settings.classList.add("hidden");
album.classList.remove("hidden");
history.pushState({}, null, "/images/");
} else {
album.classList.add("hidden");
}
}
const register_settings_storage = async () => {
optionElements.forEach((element) => {
if (element.type == "textarea") {
@ -891,14 +926,18 @@ colorThemes.forEach((themeOption) => {
function count_tokens(model, text) {
if (model) {
if (window.llamaTokenizer)
if (model.startsWith("llama2") || model.startsWith("codellama")) {
return llamaTokenizer?.encode(text).length;
return llamaTokenizer.encode(text).length;
}
if (window.mistralTokenizer)
if (model.startsWith("mistral") || model.startsWith("mixtral")) {
return mistralTokenizer?.encode(text).length;
return mistralTokenizer.encode(text).length;
}
}
return GPTTokenizer_cl100k_base?.encode(text).length;
if (window.GPTTokenizer_cl100k_base) {
return GPTTokenizer_cl100k_base.encode(text).length;
}
}
function count_words(text) {
@ -1232,12 +1271,12 @@ if (SpeechRecognition) {
}
let startValue;
let lastValue;
let timeoutHandle;
let lastDebounceTranscript;
recognition.onstart = function() {
microLabel.classList.add("recognition");
startValue = messageInput.value;
lastValue = "";
lastDebounceTranscript = "";
timeoutHandle = window.setTimeout(may_stop, 8000);
};
recognition.onend = function() {
@ -1248,22 +1287,27 @@ if (SpeechRecognition) {
return;
}
window.clearTimeout(timeoutHandle);
let newText;
Array.from(event.results).forEach((result) => {
newText = result[0].transcript;
if (newText && newText != lastValue) {
messageInput.value = `${startValue ? startValue+"\n" : ""}${newText.trim()}`;
if (result.isFinal) {
lastValue = newText;
startValue = messageInput.value;
messageInput.focus();
}
messageInput.style.height = messageInput.scrollHeight + "px";
messageInput.scrollTop = messageInput.scrollHeight;
let result = event.results[event.resultIndex];
let isFinal = result.isFinal && (result[0].confidence > 0);
let transcript = result[0].transcript;
if (isFinal) {
if(transcript == lastDebounceTranscript) {
return;
}
});
window.clearTimeout(timeoutHandle);
timeoutHandle = window.setTimeout(may_stop, newText ? 8000 : 5000);
lastDebounceTranscript = transcript;
}
if (transcript) {
messageInput.value = `${startValue ? startValue+"\n" : ""}${transcript.trim()}`;
if (isFinal) {
startValue = messageInput.value;
messageInput.focus();
}
messageInput.style.height = messageInput.scrollHeight + "px";
messageInput.scrollTop = messageInput.scrollHeight;
}
timeoutHandle = window.setTimeout(may_stop, transcript ? 8000 : 5000);
};
microLabel.addEventListener("click", () => {

View File

@ -78,6 +78,7 @@ class AbstractProvider(BaseProvider):
timeout=kwargs.get("timeout")
)
@classmethod
def get_parameters(cls) -> dict:
return signature(
cls.create_async_generator if issubclass(cls, AsyncGeneratorProvider) else
@ -107,7 +108,9 @@ class AbstractProvider(BaseProvider):
continue
args += f"\n {name}"
args += f": {get_type_name(param.annotation)}" if param.annotation is not Parameter.empty else ""
args += f' = "{param.default}"' if param.default == "" else f" = {param.default}" if param.default is not Parameter.empty else ""
default_value = f'"{param.default}"' if isinstance(param.default, str) else param.default
args += f" = {default_value}" if param.default is not Parameter.empty else ""
args += ","
return f"g4f.Provider.{cls.__name__} supports: ({args}\n)"

View File

@ -33,9 +33,14 @@ class StreamSession(ClientSession):
**DEFAULT_HEADERS,
**headers
}
connect = None
if isinstance(timeout, tuple):
connect, timeout = timeout;
if timeout is not None:
timeout = ClientTimeout(timeout, connect)
super().__init__(
**kwargs,
timeout=ClientTimeout(timeout) if timeout else None,
timeout=timeout,
response_class=StreamResponse,
connector=get_connector(connector, proxies.get("all", proxies.get("https"))),
headers=headers

View File

@ -1,6 +1,11 @@
from __future__ import annotations
from curl_cffi.requests import AsyncSession, Response, CurlMime
from curl_cffi.requests import AsyncSession, Response
try:
from curl_cffi.requests import CurlMime
has_curl_mime = True
except ImportError:
has_curl_mime = False
from typing import AsyncGenerator, Any
from functools import partialmethod
import json
@ -78,6 +83,11 @@ class StreamSession(AsyncSession):
patch = partialmethod(request, "PATCH")
delete = partialmethod(request, "DELETE")
class FormData(CurlMime):
def add_field(self, name, data=None, content_type: str = None, filename: str = None) -> None:
self.addpart(name, content_type=content_type, filename=filename, data=data)
if has_curl_mime:
class FormData(CurlMime):
def add_field(self, name, data=None, content_type: str = None, filename: str = None) -> None:
self.addpart(name, content_type=content_type, filename=filename, data=data)
else:
class FormData():
def __init__(self) -> None:
raise RuntimeError("CurlMimi in curl_cffi is missing | pip install -U g4f[curl_cffi]")

View File

@ -12,8 +12,5 @@
"pack": "^2.2.0",
"web": "^0.0.2",
"webpack-cli": "^5.1.4"
},
"bundleDependencies": [
"@xenova/transformers"
]
}
}

View File

@ -74,6 +74,9 @@ EXTRA_REQUIRE = {
],
"local": [
"gpt4all"
],
"curl_cffi": [
"curl_cffi>=0.6.2",
]
}