diff --git a/klite.embd b/klite.embd index f50e782b1..df3106120 100644 --- a/klite.embd +++ b/klite.embd @@ -6,7 +6,7 @@ It requires no dependencies, installation or setup. Just copy this single static HTML file anywhere and open it in a browser, or from a webserver. Please go to https://github.com/LostRuins/lite.koboldai.net for updates on Kobold Lite. Kobold Lite is under the AGPL v3.0 License unless otherwise exempted. Please do not remove this line. -Current version: 106 +Current version: 107 -Concedo --> @@ -3110,6 +3110,30 @@ Current version: 106 console.log("beep sound"); } + let notify_allowed = false; + function shownotify() + { + if ("Notification" in window) { + // Request permission to show notifications + if (Notification.permission === "granted" || notify_allowed) { + var notification = new Notification("Kobold Lite", { + body: "Text Generation Completed!" + }); + } else { + Notification.requestPermission().then(function (permission) { + if (permission === "granted") { + notify_allowed = true; + console.log("Notification permission granted"); + } else { + console.log("Notification permission denied"); + } + }); + } + } else { + console.log("Notification API not supported in this browser"); + } + } + function compare_version_str(a, b) { var i, diff; var regExStrip0 = /(\.0+)+$/; @@ -3261,6 +3285,9 @@ Current version: 106 const a1111_options_endpoint = "/sdapi/v1/options"; const a1111_txt2img_endpoint = "/sdapi/v1/txt2img"; + const xtts_gen_endpoint = "/tts_to_audio"; + const xtts_voices_endpoint = "/speakers_list"; + //support for quick news updates const horde_news_endpoint = "https://hordenews.concedo.workers.dev" @@ -3317,6 +3344,7 @@ Current version: 106 var custom_claude_key = ""; var custom_claude_model = ""; var a1111_base_url = "http://localhost:7860"; + var xtts_base_url = " http://localhost:8020"; var uses_cors_proxy = false; //we start off attempting a direct connection. switch to proxy if that fails var synchro_polled_response = null; var synchro_pending_stream = ""; //used for token pseduo streaming for kobold api only @@ -3367,8 +3395,9 @@ Current version: 106 instruct_has_markdown: true, placeholder_tags: true, persist_session: true, - speech_synth: 0, //0 is disabled + speech_synth: 0, //0 is disabled, 100 is xtts beep_on: false, + notify_on: false, narrate_both_sides: false, image_styles: "", grammar:"", @@ -7386,9 +7415,12 @@ Current version: 106 } else { console.log("No speech synth available"); } + ttshtml += ""; document.getElementById("ttsselect").innerHTML = ttshtml; document.getElementById("ttsselect").value = localsettings.speech_synth; + toggle_tts_mode(); document.getElementById("beep_on").checked = localsettings.beep_on; + document.getElementById("notify_on").checked = localsettings.notify_on; document.getElementById("narrate_both_sides").checked = localsettings.narrate_both_sides; toggle_opmode(); @@ -7573,6 +7605,7 @@ Current version: 106 localsettings.speech_synth = document.getElementById("ttsselect").value; localsettings.beep_on = (document.getElementById("beep_on").checked?true:false); + localsettings.notify_on = (document.getElementById("notify_on").checked?true:false); localsettings.narrate_both_sides = (document.getElementById("narrate_both_sides").checked?true:false); localsettings.auto_ctxlen = (document.getElementById("auto_ctxlen").checked ? true : false); localsettings.auto_genamt = (document.getElementById("auto_genamt").checked ? true : false); @@ -8312,6 +8345,102 @@ Current version: 106 } } + var xtts_is_connected = false; + function fetch_xtts_voices(silent) + { + if(!xtts_is_connected) + { + fetch(xtts_base_url + xtts_voices_endpoint) + .then(x => x.json()) + .then(data => { + console.log(data); + //repopulate our voices list + let dropdown = document.getElementById("xtts_voices"); + let selectionhtml = ``; + for (var i = 0; i < data.length; ++i) { + selectionhtml += ``; + } + dropdown.innerHTML = selectionhtml; + xtts_is_connected = true; + }).catch((error) => { + xtts_is_connected = false; + if(!silent) + { + msgbox("XTTS Connect Error: " + error+"\nCheck XTTS API Server endpoint URL.\n"); + } + }); + } + } + + function toggle_tts_mode() + { + if(document.getElementById("ttsselect").value==100) + { + document.getElementById("xtts_container").classList.remove("hidden"); + }else{ + document.getElementById("xtts_container").classList.add("hidden"); + } + } + function set_xtts_url() + { + inputBox("Enter XTTS API Server URL.","XTTS API Server URL",xtts_base_url,"Input XTTS API Server URL", ()=>{ + let userinput = getInputBoxValue(); + userinput = userinput.trim(); + if(userinput!="" && userinput.slice(-1)=="/") + { + userinput = userinput.slice(0, -1); + } + if (userinput != null && userinput!="") { + xtts_base_url = userinput.trim(); + xtts_is_connected = false; + fetch_xtts_voices(false); + } + },false); + } + function tts_speak(text) + { + if(localsettings.speech_synth==100) //xtts api server + { + if(xtts_is_connected) + { + const audioContext = new (window.AudioContext || window.webkitAudioContext)(); + let xtts_payload = { + "text": text, + "speaker_wav": document.getElementById("xtts_voices").value, + "language": "EN" + }; + + fetch(xtts_base_url + xtts_gen_endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(xtts_payload), + }) + .then(response => response.arrayBuffer()) + .then(data => { + return audioContext.decodeAudioData(data); + }) + .then(decodedData => { + const playSound = audioContext.createBufferSource(); + playSound.buffer = decodedData; + playSound.connect(audioContext.destination); + playSound.start(audioContext.currentTime); + }).catch((error) => { + console.log("XTTS Speak Error: " + error); + }); + } + } + else + { + if ('speechSynthesis' in window) { + let utterance = new window.SpeechSynthesisUtterance(text); + utterance.voice = window.speechSynthesis.getVoices()[localsettings.speech_synth - 1]; + window.speechSynthesis.speak(utterance); + } + } + } + function submit_generation() { let newgen = document.getElementById("input_text").value; @@ -8323,12 +8452,11 @@ Current version: 106 waiting_for_autosummary = false; idle_timer = 0; idle_triggered_counter = 0; - if (localsettings.speech_synth > 0 && 'speechSynthesis' in window) { + if (localsettings.speech_synth > 0) + { if(localsettings.narrate_both_sides) { - let utterance = new window.SpeechSynthesisUtterance(newgen); - utterance.voice = window.speechSynthesis.getVoices()[localsettings.speech_synth - 1]; - window.speechSynthesis.speak(utterance); + tts_speak(newgen); } } @@ -9734,6 +9862,11 @@ Current version: 106 } } + //second pass for trimming whitespace + if (localsettings.trimwhitespace) { + gentxt = gentxt.replace(/[\t\r\n ]+$/, ''); + } + let gentxtspeak = gentxt; if (pending_context_preinjection != "") { if(gentxt!="" && gentxt[0]!=" " && localsettings.opmode==3) @@ -9745,15 +9878,13 @@ Current version: 106 pending_context_preinjection = ""; } - if (localsettings.speech_synth > 0 && 'speechSynthesis' in window) + if (localsettings.speech_synth > 0) { if(localsettings.narrate_both_sides) { gentxtspeak = gentxt; } - let utterance = new window.SpeechSynthesisUtterance(gentxtspeak); - utterance.voice = window.speechSynthesis.getVoices()[localsettings.speech_synth - 1]; - window.speechSynthesis.speak(utterance); + tts_speak(gentxt); } if(gentxt!="") @@ -9765,6 +9896,10 @@ Current version: 106 { playbeep(); } + if(localsettings.notify_on) + { + shownotify(); + } let lastreq = "Last request served by " + genworker + " using "+genmdl+ ""+(genkudos>0?(" for " + genkudos + " kudos"):"")+" in " + getTimeTaken() + " seconds."; document.getElementById("lastreq").innerHTML = lastreq; document.getElementById("lastreq2").innerHTML = lastreq; @@ -12716,8 +12851,16 @@ Current version: 106
TTS ?Enable Text-To-Speech to have your story automatically read to you.
- +
Narrate Both Sides
@@ -12727,6 +12870,10 @@ Current version: 106
Beep on Done
+
+
Notify on Done
+ +
diff --git a/koboldcpp.py b/koboldcpp.py index 933c06c27..57dfb2208 100755 --- a/koboldcpp.py +++ b/koboldcpp.py @@ -1268,9 +1268,11 @@ def show_new_gui(): def makefileentry(parent, text, searchtext, var, row=0, width=200, filetypes=[], onchoosefile=None, singlerow=False, tooltiptxt=""): makelabel(parent, text, row,0,tooltiptxt) def getfilename(var, text): - var.set(askopenfilename(title=text,filetypes=filetypes)) - if onchoosefile: - onchoosefile(var.get()) + fnam = askopenfilename(title=text,filetypes=filetypes) + if fnam: + var.set(fnam) + if onchoosefile: + onchoosefile(var.get()) entry = ctk.CTkEntry(parent, width, textvariable=var) button = ctk.CTkButton(parent, 50, text="Browse", command= lambda a=var,b=searchtext:getfilename(a,b)) if singlerow: