Allow override config for gguf files when reloading in admin mode, updated lite, fixed typo (+1 squashed commits)

Squashed commits:

[fe14845cc] Allow override config for gguf files when reloading in admin mode, updated lite (+2 squashed commit)

Squashed commit:

[9ded66aa5] Allow override config for gguf files when reloading in admin mode

[9597f6a34] update lite
This commit is contained in:
Concedo 2025-06-14 10:37:36 +08:00
parent bfb47cbcd8
commit 238be98efa
2 changed files with 387 additions and 26 deletions

View file

@ -12,7 +12,7 @@ Current version indicated by LITEVER below.
--> -->
<script> <script>
const LITEVER = 252; const LITEVER = 253;
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
var localflag = urlParams.get('local'); //this will be replaced automatically in embedded kcpp var localflag = urlParams.get('local'); //this will be replaced automatically in embedded kcpp
const STORAGE_PREFIX = (localflag?"e_":"")+"kaihordewebui_"; const STORAGE_PREFIX = (localflag?"e_":"")+"kaihordewebui_";
@ -2206,6 +2206,18 @@ Current version indicated by LITEVER below.
.color_pink { .color_pink {
color: #ffbdbd; color: #ffbdbd;
} }
.color_wordsearch_target
{
color: violet;
text-decoration: underline;
text-decoration-color: violet;
}
.color_wordsearch_surrounding
{
color: yellow;
text-decoration: underline;
text-decoration-color: yellow;
}
.bg_black { .bg_black {
background-color: #202020; background-color: #202020;
@ -3085,6 +3097,7 @@ Current version indicated by LITEVER below.
const HD_RES_PX = 768; const HD_RES_PX = 768;
const VHD_RES_PX = 960; const VHD_RES_PX = 960;
const NO_HD_RES_PX = 512; const NO_HD_RES_PX = 512;
const PREVIEW_RES_PX = 200;
const AVATAR_PX = 384; const AVATAR_PX = 384;
const SAVE_SLOTS = 10; const SAVE_SLOTS = 10;
const num_regex_rows = 4; const num_regex_rows = 4;
@ -3367,6 +3380,7 @@ Current version indicated by LITEVER below.
websearch_multipass: false, websearch_multipass: false,
websearch_retain: false, websearch_retain: false,
websearch_template: "", websearch_template: "",
wordsearch_enabled: false,
max_context_length: (localflag?4096:2048), max_context_length: (localflag?4096:2048),
max_length: (localflag?512:256), max_length: (localflag?512:256),
@ -11150,6 +11164,7 @@ Current version indicated by LITEVER below.
el.value = values[i]; el.value = values[i];
dropdown.appendChild(el); dropdown.appendChild(el);
} }
change_admin_config_selection();
document.getElementById("admincontainer").classList.remove("hidden"); document.getElementById("admincontainer").classList.remove("hidden");
} }
else else
@ -11175,6 +11190,36 @@ Current version indicated by LITEVER below.
} }
} }
function change_admin_config_selection()
{
let targetfile = document.getElementById("adminconfigdropdown").value;
if(targetfile && targetfile.endsWith(".gguf"))
{
let overridedropdown = document.getElementById("adminconfigoverridedropdown");
overridedropdown.classList.remove("hidden");
var dropdown = document.getElementById("adminconfigdropdown");
for (var i = overridedropdown.options.length; i >= 0; i--) {
var option = overridedropdown.options[i];
overridedropdown.remove(option);
}
var el = document.createElement("option");
el.textContent = "Default Config";
el.value = "";
overridedropdown.appendChild(el);
for(var i=0;i<dropdown.options.length;++i)
{
let str = dropdown.options[i].value;
if(str.endsWith(".gguf") || str=="unload_model")
{continue;}
var el = document.createElement("option");
el.textContent = str;
el.value = str;
overridedropdown.appendChild(el);
}
}else{
document.getElementById("adminconfigoverridedropdown").classList.add("hidden");
}
}
var adminrebootflag = 0; var adminrebootflag = 0;
function trigger_admin_reload() function trigger_admin_reload()
{ {
@ -11185,7 +11230,14 @@ Current version indicated by LITEVER below.
msgbox("No config file was selected."); msgbox("No config file was selected.");
return; return;
} }
let header = {'Content-Type': 'application/json'}; let header = {'Content-Type': 'application/json'};
let payload = {"filename": targetfile};
if(!document.getElementById("adminconfigoverridedropdown").classList.contains("hidden"))
{
payload["overrideconfig"] = document.getElementById("adminconfigoverridedropdown").value;
}
if(last_admin_key!="") if(last_admin_key!="")
{ {
header['Authorization'] = 'Bearer ' + last_admin_key; header['Authorization'] = 'Bearer ' + last_admin_key;
@ -11193,9 +11245,7 @@ Current version indicated by LITEVER below.
fetch(custom_kobold_endpoint + koboldcpp_admin_reload_endpoint, { fetch(custom_kobold_endpoint + koboldcpp_admin_reload_endpoint, {
method: 'POST', method: 'POST',
headers: header, headers: header,
body: JSON.stringify({ body: JSON.stringify(payload)
"filename": targetfile
})
}) })
.then(x => x.json()) .then(x => x.json())
.then(values => { .then(values => {
@ -12353,6 +12403,7 @@ Current version indicated by LITEVER below.
document.getElementById("run_in_background").checked = run_in_background; document.getElementById("run_in_background").checked = run_in_background;
document.getElementById("auto_ctxlen").checked = localsettings.auto_ctxlen; document.getElementById("auto_ctxlen").checked = localsettings.auto_ctxlen;
document.getElementById("auto_genamt").checked = localsettings.auto_genamt; document.getElementById("auto_genamt").checked = localsettings.auto_genamt;
document.getElementById("wordsearch_toggle").checked = localsettings.wordsearch_enabled;
if(localflag) if(localflag)
{ {
document.getElementById("auto_ctxlen_panel").classList.add("hidden"); document.getElementById("auto_ctxlen_panel").classList.add("hidden");
@ -12943,6 +12994,7 @@ Current version indicated by LITEVER below.
localsettings.voice_langcode = document.getElementById("voice_langcode").value; localsettings.voice_langcode = document.getElementById("voice_langcode").value;
localsettings.auto_ctxlen = (document.getElementById("auto_ctxlen").checked ? true : false); localsettings.auto_ctxlen = (document.getElementById("auto_ctxlen").checked ? true : false);
localsettings.auto_genamt = (document.getElementById("auto_genamt").checked ? true : false); localsettings.auto_genamt = (document.getElementById("auto_genamt").checked ? true : false);
localsettings.wordsearch_enabled = (document.getElementById("wordsearch_toggle").checked ? true : false);
localsettings.image_styles = document.getElementById("imagestyleinput").value; localsettings.image_styles = document.getElementById("imagestyleinput").value;
localsettings.image_negprompt = document.getElementById("negpromptinput").value; localsettings.image_negprompt = document.getElementById("negpromptinput").value;
@ -13240,9 +13292,20 @@ Current version indicated by LITEVER below.
function toggle_uistyle() function toggle_uistyle()
{ {
//show or hide the 'Customize UI' button based on whether the Aesthetic Instruct UI Mode is active or not. //show or hide the 'Customize UI' button based on whether the Aesthetic Instruct UI Mode is active or not.
if (document.getElementById('gui_type').value==2) { document.getElementById('btn_aesthetics').classList.remove('hidden'); } if (document.getElementById('gui_type').value == 2) {
else { document.getElementById('btn_aesthetics').classList.add('hidden'); } document.getElementById('btn_aesthetics').classList.remove('hidden');
}
else {
document.getElementById('btn_aesthetics').classList.add('hidden');
}
document.getElementById("guitypedesc").innerText = get_theme_desc(document.getElementById('gui_type').value); document.getElementById("guitypedesc").innerText = get_theme_desc(document.getElementById('gui_type').value);
if (document.getElementById('gui_type').value == 0) {
document.getElementById('classicmodeoptions').classList.remove('hidden');
}
else {
document.getElementById('classicmodeoptions').classList.add('hidden');
}
} }
function select_welcome_ui() function select_welcome_ui()
@ -13345,8 +13408,12 @@ Current version indicated by LITEVER below.
document.getElementById('gui_type').value = 0; document.getElementById('gui_type').value = 0;
} }
if (document.getElementById('gui_type').value==2) { document.getElementById('btn_aesthetics').classList.remove('hidden'); } if (document.getElementById('gui_type').value == 2) {
else { document.getElementById('btn_aesthetics').classList.add('hidden'); } document.getElementById('btn_aesthetics').classList.remove('hidden');
}
else {
document.getElementById('btn_aesthetics').classList.add('hidden');
}
toggle_uistyle(); toggle_uistyle();
} }
@ -13642,6 +13709,17 @@ Current version indicated by LITEVER below.
} }
} }
function simplecssexample()
{
let simplemodscript = `/* This custom CSS changes all buttons to yellow */
.btn-primary {
color: black!important;
background-color: yellow!important;
border-color: black!important;
}
`;
document.getElementById("inputboxcontainerinputarea").value = simplemodscript;
}
function simplemodexample() function simplemodexample()
{ {
let simplemodscript = `// This mod changes your top menu to a yellow gradient, then displays the current temperature setting as a popup\n` let simplemodscript = `// This mod changes your top menu to a yellow gradient, then displays the current temperature setting as a popup\n`
@ -13665,7 +13743,7 @@ Current version indicated by LITEVER below.
} }
} }
} catch (e) { } } catch (e) { }
let txt = `Here, you can apply third-party mod scripts shared by other users.<br><br><span class='color_red'>Caution: This mod will have full access to your story and API keys, so only run third-party mods that you trust!<br><br>Mods must always be manually applied every time unless 'Apply Mod On Startup' is selected. If a startup mod breaks KoboldAI Lite, add ?resetmod=1 to the url to uninstall it.</span><br><br>Want to start modding? <a href='#' class='color_blueurl' onclick='simplemodexample()'>Click here</a> to load a simple example mod.<br><br><div><span>Apply Mod On Startup? (Extreme Caution!) </span><input type='checkbox' id='apply_mod_on_start' style='vertical-align: top;' ${applyonstart?'checked':''}></div>`; let txt = `Here, you can apply third-party mod scripts shared by other users.<br><br><span class='color_red'>Caution: This mod will have full access to your story and API keys, so only run third-party mods that you trust!<br><br>Mods must always be manually applied every time unless 'Apply Mod On Startup' is selected. If a startup mod breaks KoboldAI Lite, add ?resetmod=1 to the url to uninstall it.</span><br><br>Want to start modding? <a href='#' class='color_blueurl' onclick='simplemodexample()'>Click here</a> to load a simple example mod.<br><br><div><span>Apply Mod On Startup? (Extreme Caution!) </span><input type='checkbox' id='apply_mod_on_start' style='vertical-align: top;' ${applyonstart?'checked':''}></div><div><button type="button" class="btn btn-primary" onclick="load_inputbox_from_file()">Import UserMod From File</button></div>`;
inputBoxOkCancel(txt,"Apply Third-Party Mod",currmod,"Paste Mod Script Here",()=>{ inputBoxOkCancel(txt,"Apply Third-Party Mod",currmod,"Paste Mod Script Here",()=>{
let userinput = getInputBoxValue().trim(); let userinput = getInputBoxValue().trim();
applyonstart = (document.getElementById("apply_mod_on_start").checked?true:false); applyonstart = (document.getElementById("apply_mod_on_start").checked?true:false);
@ -13683,18 +13761,42 @@ Current version indicated by LITEVER below.
}); });
}); });
} }
function load_inputbox_from_file()
{
promptUserForLocalFile((fileDetails) => {
try {
let { file, fileName, ext, content, plaintext } = fileDetails;
if(plaintext && plaintext!="")
{
if(document.getElementById("inputboxcontainerinputarea").classList.contains("hidden"))
{
document.getElementById("inputboxcontainerinput").value = plaintext;
}
else
{
document.getElementById("inputboxcontainerinputarea").value = plaintext;
}
}
}
catch (e) {
console.log("Load Inputbox File Failed: " + e);
}
});
}
function sanitize_css(input) function sanitize_css(input)
{ {
input = input.replace(/<\s*\/?\s*\w+\s*[^>]*>/gi, ""); //replace html tags input = input.replace(/<\s*\/?\s*\w+\s*[^>]*>/gi, "");
input = input.replace(/</g, ""); // Remove any remaining `<` characters input = input.replace(/</g, "");
input = input.replace(/(?:javascript|data|vbscript|file):/gi, ""); input = input.replace(/(?:javascript|vbscript|file):/gi, "");
//remove all data elements that aren't jpg, png, gif, webp;base64
input = input.replace(/data:(?!image\/(jpeg|png|gif|webp);base64,)[^)'";\s]*/gi, "");
return input; return input;
} }
function apply_custom_css() function apply_custom_css()
{ {
indexeddb_load("savedcustomcss","").then(currcss=>{ indexeddb_load("savedcustomcss","").then(currcss=>{
inputBoxOkCancel("Here, you can apply third-party custom CSS styles shared by other users.<br><br><span class='color_red'>Caution: This will modify the existing document, so only use third-party CSS styles that you trust! Custom CSS is persistent, but can be reset in this menu.</span><br><br>You can return to default styles by clearing the box below. If you get stuck, add ?resetcss=1 to the URL to clear custom styles.","Apply Custom CSS Styles",currcss,"Paste CSS Styles Here",()=>{ inputBoxOkCancel(`Here, you can apply third-party custom CSS styles shared by other users.<br><br><span class='color_red'>Caution: This will modify the existing document, so only use third-party CSS styles that you trust! Custom CSS is persistent, but can be reset in this menu.</span><br><br>Need help? <a href='#' class='color_blueurl' onclick='simplecssexample()'>Click here</a> to load a simple example CSS theme. You can return to default styles by clearing the box below. If you get stuck, add ?resetcss=1 to the URL to clear custom styles. <div><button type="button" class="btn btn-primary" onclick="load_inputbox_from_file()">Import CSS From File</button></div>`,"Apply Custom CSS Styles",currcss,"Paste CSS Styles Here",()=>{
let userinput = sanitize_css(getInputBoxValue().trim()); let userinput = sanitize_css(getInputBoxValue().trim());
indexeddb_save("savedcustomcss", userinput); indexeddb_save("savedcustomcss", userinput);
let styleElement = document.getElementById('custom_css'); let styleElement = document.getElementById('custom_css');
@ -13783,6 +13885,8 @@ Current version indicated by LITEVER below.
document.getElementById("input_text").value = ""; document.getElementById("input_text").value = "";
document.getElementById("corpo_cht_inp").value = ""; document.getElementById("corpo_cht_inp").value = "";
document.getElementById("cht_inp").value = ""; document.getElementById("cht_inp").value = "";
let wsresultsContainer = document.getElementById("wordsearch_results");
if(wsresultsContainer){wsresultsContainer.innerHTML="";}
chat_resize_input(); chat_resize_input();
image_db = {}; image_db = {};
interrogation_db = {}; interrogation_db = {};
@ -17378,7 +17482,7 @@ Current version indicated by LITEVER below.
} }
function render_image_html(data, pend_txt = "", siclass="storyimgfloat") { function render_image_html(data, pend_txt = "", siclass="storyimgfloat") {
var dim = (localsettings.opmode == 2 ? 160 : 180); //adventure mode has smaller pictures var dim = PREVIEW_RES_PX; //image preview. adventure mode has smaller pictures
dimW = dim; dimW = dim;
dimH = dim; dimH = dim;
let reinvertcolor = localsettings.invert_colors?" invert_colors":""; let reinvertcolor = localsettings.invert_colors?" invert_colors":"";
@ -19792,6 +19896,14 @@ Current version indicated by LITEVER below.
{ {
document.getElementById("searchingtxt").classList.add("hidden"); document.getElementById("searchingtxt").classList.add("hidden");
} }
if(localsettings.wordsearch_enabled)
{
document.getElementById("wordsearch_panel").classList.remove("hidden");
}else
{
document.getElementById("wordsearch_panel").classList.add("hidden");
}
} }
function render_corpo_welcome() function render_corpo_welcome()
@ -21702,7 +21814,7 @@ Current version indicated by LITEVER below.
} }
this.code_block_background = 'rgb(0, 0, 0)'; this.code_block_background = 'rgb(0, 0, 0)';
this.code_block_foreground = 'rgb(180, 35, 40)'; this.code_block_foreground = 'rgb(210, 50, 50)';
} }
padding() { return `${this.background_padding[2]}px ${this.background_padding[1]}px ${this.background_padding[3]}px ${this.background_padding[0]}px`; } padding() { return `${this.background_padding[2]}px ${this.background_padding[1]}px ${this.background_padding[3]}px ${this.background_padding[0]}px`; }
@ -22005,6 +22117,11 @@ Current version indicated by LITEVER below.
`<style> `<style>
.you-portrait-image`+classSuffixStr+` {margin: 10px 6px; background:url(`+ as.you_portrait +`); background-clip: content-box; background-position: 50% 50%; background-size: 100% 100%; background-origin: content-box; background-repeat: no-repeat; border:none;} .you-portrait-image`+classSuffixStr+` {margin: 10px 6px; background:url(`+ as.you_portrait +`); background-clip: content-box; background-position: 50% 50%; background-size: 100% 100%; background-origin: content-box; background-repeat: no-repeat; border:none;}
.AI-portrait-image`+classSuffixStr+` {margin: 10px 6px; background:url(`+ (as.AI_portrait!="default"?as.AI_portrait:niko_square) +`); background-clip: content-box; background-position: 50% 50%; background-size: 100% 100%; background-origin: content-box; background-repeat: no-repeat; border:none;} .AI-portrait-image`+classSuffixStr+` {margin: 10px 6px; background:url(`+ (as.AI_portrait!="default"?as.AI_portrait:niko_square) +`); background-clip: content-box; background-position: 50% 50%; background-size: 100% 100%; background-origin: content-box; background-repeat: no-repeat; border:none;}
code
{
color: ${as.code_block_foreground};
background-color: ${as.code_block_background};
}
</style> </style>
`; `;
@ -22167,7 +22284,7 @@ Current version indicated by LITEVER below.
}); });
} }
else { else {
blocks[i] = blocks[i].replaceAll('```', '`').replaceAll('``', '`').replace(/`(.*?)`/g, function (m,m2) {return `<code style='background-color:black'>${m2.replace(/[“”]/g, "\"")}</code>`;}); //remove fancy quotes too blocks[i] = blocks[i].replaceAll('```', '`').replaceAll('``', '`').replace(/`(.*?)`/g, function (m,m2) {return `<code>${m2.replace(/[“”]/g, "\"")}</code>`;}); //remove fancy quotes too
} }
} }
return [blocks.join(''),codestashes]; return [blocks.join(''),codestashes];
@ -22194,7 +22311,7 @@ Current version indicated by LITEVER below.
} }
function updateTextPreview() { function updateTextPreview() {
let preview = `You are Mikago, a prestigious bot that's a supervillain.\n\nRoleplay in first person, be prestigious, don't be a bot. This is a fantasy world.\n\nCode blocks should be wrapped in triple backticks, like so:\n\`\`\`\n<Some_\n-- multiline\n--- code here$\n\`\`\`\n[AI_REPLY]\n*takes my hat off to greet the squad* "Greetings, I am Mikago, the prestigious!" *bows to the crew*\n*clears my throat* "Now, I'm sure there are many questions, but all will be answered in due time." *deep breath*\n[USER_REPLY]\n*draws my sword* "Yes. You should know the code to calculate the factorial of a number."\nThe crew also draws their weapons and point them at you, not giving you any space.\n[AI_REPLY]\n*backs off* "Woah, easy there.." *makes some steps backwards, but then stops*\n"I would normally take this as an insult to my prestige, but I understand your caution.." *takes a deep breath*\n"Well, if it's to prove myself, here goes the python code to calculate the factorial of a number.."\n\nMikago opens a live-code-portal with his magic and writes the code that was requested.\n\`\`\`\ndef factorial(n):\n if n == 0:\n return 1\n else:\n return n * factorial(n-1)\n\`\`\`\n*looks at you, getting impatient* "Are we ok now.. or do you want me to write the code of a game next?"\n[USER_REPLY]\n*sheathes my sword and approaches for a hug* "Oh, Mikago, my old friend, it is really you!"`; let preview = `You are Mikago, a prestigious bot that's a supervillain.\n\nRoleplay in first person, be prestigious, don't be a bot. This is a fantasy world.\n\nCode blocks should be wrapped in triple backticks, like so:\n\`\`\`\n-- multiline\n--- code here\n\`\`\`\n[AI_REPLY]\n*takes my hat off to greet the squad* "Greetings, I am Mikago, the prestigious!" *bows to the crew*\n*clears my throat* "Now, I'm sure there are many questions, but all will be answered in due time." *deep breath*\n[USER_REPLY]\n*draws my sword* "Yes. You should know the code to calculate the factorial of a number."\nThe crew also draws their weapons and point them at you, not giving you any space.\n[AI_REPLY]\n*backs off* "Woah, easy there.." *makes some steps backwards, but then stops*\n"I would normally take this as an insult to my prestige, but I understand your caution.." *takes a deep breath*\n"Well, if it's to prove myself, here goes the python code to calculate the factorial of a number.."\n\nMikago opens a live-code-portal with his magic and writes the code that was requested.\n\`\`\`\ndef factorial(n):\n if n == 0:\n return 1\n else:\n return n * factorial(n-1)\n\`\`\`\n*looks at you, getting impatient* "Are we ok now.. or do you want me to write the code of a game next?"\n[USER_REPLY]\n*sheathes my sword and approaches for a hug* "Oh, Mikago, my old friend, it is really you!"`;
if(localsettings.opmode==3) if(localsettings.opmode==3)
{ {
@ -22654,6 +22771,209 @@ Current version indicated by LITEVER below.
return searchResults; return searchResults;
} }
}); });
/** Inspired by My Ghost Writer from trincadev - https://github.com/trincadev/my_ghost_writer */
//scan through full context to ngram a possible set of candidates
function trigger_wordsearch_candidates() {
const searchBox = document.getElementById("wordsearch_input");
const resultsContainer = document.getElementById("wordsearch_results");
const sortByFreq = document.getElementById("wordsearch_sort").value=="0";
const query = searchBox.value.trim();
resultsContainer.innerHTML = "";
let ngramParser = function (text, n) {
const words = text.split(/ +/).filter(word => word.length > 0);
const ngrams = {};
let prevNgram = null;
for (let i = 0; i <= words.length - n; i++) {
let ngram = words.slice(i, i + n).join(' ');
//strip leading and trailing punctuation
ngram = ngram.replace(/^[,\.?!()\[\]{}\`:;\-\'\"—]+|[,\.?!()\[\]{}\`:;\-\'\"—]+$/g, '');
if (ngram === prevNgram) continue; // skip duplicate overlaps
prevNgram = ngram;
if (ngrams[ngram]) {
ngrams[ngram]++;
} else {
ngrams[ngram] = 1;
}
}
const sortedNgrams = Object.entries(ngrams).sort((a, b) => b[1] - a[1]);
return sortedNgrams.map(entry => ({ ng: entry[0], cnt: entry[1] }));
};
let prepare_candidates_from_text = function(query) {
const fullgametext = concat_gametext(true, "").toLowerCase();
let querylc = query.toLowerCase();
let candidateDict = {};
let textrows = fullgametext.split("\n").filter(x=>x);
for(let l=0;l<textrows.length;++l)
{
for(let i=1;i<=3;++i) //ngram of 3
{
let res = ngramParser(textrows[l],i);
if(querylc!="")
{
res = res.filter(x=>x.ng.toLowerCase().includes(querylc));
}
let lim = Math.min(res.length,250);
for(let j=0;j<lim;++j)
{
if (!candidateDict[res[j].ng]) {
candidateDict[res[j].ng] = res[j].cnt;
} else {
candidateDict[res[j].ng] += res[j].cnt;
}
}
}
}
//todo: counts are not correct because of substrings. let's manually count each item for now
for(let key in candidateDict)
{
candidateDict[key] = (fullgametext.split(key).length - 1);
}
if(querylc!="")
{
let basecount = (fullgametext.split(querylc).length - 1); //count of the pure substring
candidateDict[query] = basecount;
}
return candidateDict;
}
let candidateDict = prepare_candidates_from_text(query);
var candidateItems = Object.keys(candidateDict).map(function(key) {
return [key, candidateDict[key]];
});
if(sortByFreq)
{
candidateItems.sort(function(first, second) {
return second[1] - first[1];
});
}else
{
//sort alphabetically
candidateItems.sort(function(first, second) {
if (first[0] < second[0]) {
return -1;
}
if (first[0] > second[0]) {
return 1;
}
return 0;
});
}
for(let i=0;i<candidateItems.length;++i)
{
let key = candidateItems[i][0];
let count = candidateItems[i][1];
const btn = document.createElement("div");
btn.style.border = "1px solid #646464";
btn.innerHTML = `<a href="#"><span class="color_wordsearch_surrounding">${key} (${count})</span></a>`;
let searchstr = key;
btn.onclick = () => {
trigger_wordsearch_results(searchstr);
};
resultsContainer.appendChild(btn);
}
}
function trigger_wordsearch_candidates_key()
{
if (event.key === 'Enter') {
trigger_wordsearch_candidates();
}
}
function trigger_wordsearch_results(query) {
const resultsContainer = document.getElementById("wordsearch_results");
const gametext = document.getElementById("gametext");
query = query.trim();
resultsContainer.innerHTML = "";
if (!query) return;
let extract_surrounding_text = function(range, contextLength) {
const textNode = range.startContainer;
if (textNode.nodeType !== Node.TEXT_NODE) return;
const textContent = textNode.textContent;
const startOffset = range.startOffset;
const endOffset = range.endOffset;
// Compute raw bounds
let start = Math.max(0, startOffset - contextLength);
let end = Math.min(textContent.length, endOffset + contextLength);
// Clamp to word boundaries
start = textContent.lastIndexOf(' ', start);
end = textContent.indexOf(' ', end);
if (start === -1) start = 0;
if (end === -1) end = textContent.length;
const before = textContent.slice(start, startOffset);
const middle = textContent.slice(startOffset, endOffset);
const after = textContent.slice(endOffset,end);
return {"before":before, "middle":middle, "after":after, "full":`${before}${middle}${after}`};
}
let search_text_for_strings = function(query) { //search text nodes for all instances of query
const gametext = document.getElementById("gametext");
let matchRanges = [];
const walker = document.createTreeWalker(gametext, NodeFilter.SHOW_TEXT, null, false);
while (walker.nextNode()) {
const node = walker.currentNode;
const text = node.nodeValue;
let idx = 0;
while ((idx = text.toLowerCase().indexOf(query.toLowerCase(), idx)) !== -1) {
const range = document.createRange();
range.setStart(node, idx);
range.setEnd(node, idx + query.length);
matchRanges.push({ range, preview: text.substr(idx, query.length) });
idx += query.length;
}
}
return matchRanges;
}
let matchRanges = search_text_for_strings(query);
const topper = document.createElement("div");
topper.style.border = "1px solid #646464";
topper.innerText = `${matchRanges.length} results`;
resultsContainer.appendChild(topper);
matchRanges.forEach(({ range, preview }, i) => {
const btn = document.createElement("div");
btn.style.border = "1px solid #646464";
let nearbytext = extract_surrounding_text(range, 8); //8 chars clamped to word bounds
btn.innerHTML = `<a href="#"><span class="color_wordsearch_surrounding">${nearbytext.before}</span><span class="color_wordsearch_target">${nearbytext.middle}</span><span class="color_wordsearch_surrounding">${nearbytext.after}</span></a>`;
btn.onclick = () => {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
// Scroll the container
const rect = range.getBoundingClientRect();
const containerRect = gametext.getBoundingClientRect();
if (rect.top < containerRect.top || rect.bottom > containerRect.bottom) {
if (range.startContainer && range.startContainer.nodeType === Node.TEXT_NODE) {
const span = document.createElement("span");
span.style.display = "inline";
span.style.background = "transparent";
span.id = "temp-scroll-target";
range.insertNode(span); // Insert a temporary marker, then scroll to it
document.querySelector("#temp-scroll-target").scrollIntoView({ behavior: "smooth", block: "center" });
span.remove(); //remove temp marker
}
}
};
resultsContainer.appendChild(btn);
});
}
</script> </script>
</head> </head>
@ -22770,6 +23090,20 @@ Current version indicated by LITEVER below.
<p id="tempgtloadtxt">Loading...</p> <p id="tempgtloadtxt">Loading...</p>
<noscript><style>#tempgtloadtxt { display: none; } #gametext { white-space: normal!important; }</style><p>Sorry, KoboldAI Lite requires Javascript to function.</p></noscript> <noscript><style>#tempgtloadtxt { display: none; } #gametext { white-space: normal!important; }</style><p>Sorry, KoboldAI Lite requires Javascript to function.</p></noscript>
</span> </span>
<div class="hidden" id="wordsearch_panel" style="flex:250px">
<div style="display:flex; padding:6px">
<input title="Word Search Input" class="form-control menuinput_inline" style="width: calc(100% - 34px); margin-right:2px;" type="text" placeholder="WordSearch" value="" onkeyup="trigger_wordsearch_candidates_key();" id="wordsearch_input">
<button title="Perform WordSearch" type="button" class="btn btn-primary" style="width:30px;padding:4px 4px;" onclick="trigger_wordsearch_candidates();">🔎</button>
</div>
<div style="display:flex; padding:6px">
<p style="margin-right:6px">Sort: </p>
<select title="Sort" style="padding:2px; font-size:14px; height:24px; width: calc(100% - 50px);" class="form-control" id="wordsearch_sort">
<option value="0">Frequency</option>
<option value="1">Alphabetical</option>
</select>
</div>
<div id="wordsearch_results" style="height:calc(100% - 98px);overflow-y: auto;"></div>
</div>
</div> </div>
</div> </div>
@ -22883,6 +23217,8 @@ Current version indicated by LITEVER below.
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="popupcontainer flex hidden" id="memorycontainer"> <div class="popupcontainer flex hidden" id="memorycontainer">
@ -23129,6 +23465,10 @@ Current version indicated by LITEVER below.
<option id="uipicker_corpo" value="3">Corpo Theme</option> <option id="uipicker_corpo" value="3">Corpo Theme</option>
</select> </select>
<button type="button" class="btn btn-primary" id="btn_aesthetics" onclick="openAestheticUISettingsMenu()" style="border-color: #bbbbbb; width:100%; height:30px; padding:0px 8px; margin: 3px 0 3px 0;">⚙️ Customize</button> <button type="button" class="btn btn-primary" id="btn_aesthetics" onclick="openAestheticUISettingsMenu()" style="border-color: #bbbbbb; width:100%; height:30px; padding:0px 8px; margin: 3px 0 3px 0;">⚙️ Customize</button>
<div id="classicmodeoptions" style="margin:4px;" class="settinglabel hidden">
<div class="justifyleft settingsmall">Word Frequency Analysis <span class="helpicon">?<span class="helptext">Shows a tool that displays word frequency and locations, inspired by trincadev MyGhostWriter. Counts across newlines may be inaccurate.</span></span></div>
<input title="WordSearch Tool" type="checkbox" id="wordsearch_toggle" style="margin:0px 0px 0px 0px;">
</div>
<div id="guitypedesc" class="settingsdesctxt"></div> <div id="guitypedesc" class="settingsdesctxt"></div>
</div> </div>
</div> </div>
@ -24925,7 +25265,9 @@ Current version indicated by LITEVER below.
<div> <div>
<b class="color_white" style="padding: 5px;">Change Loaded Model / Config:</b><br> <b class="color_white" style="padding: 5px;">Change Loaded Model / Config:</b><br>
<div style="display:flex;padding: 5px;"> <div style="display:flex;padding: 5px;">
<select title="Select New Config" style="padding:4px; width:calc(100% - 150px)" class="form-control" id="adminconfigdropdown"> <select title="Select New Config" style="padding:4px; width:100%" class="form-control" id="adminconfigdropdown" onchange="change_admin_config_selection()">
</select>
<select title="Override Config" style="padding:4px; width:100%" class="form-control hidden" id="adminconfigoverridedropdown">
</select> </select>
<button type="button" style="margin-left:2px;width:146px" class="btn btn-primary" onclick="trigger_admin_reload()">Reload KoboldCpp</button> <button type="button" style="margin-left:2px;width:146px" class="btn btn-primary" onclick="trigger_admin_reload()">Reload KoboldCpp</button>
</div> </div>
@ -25246,7 +25588,7 @@ Current version indicated by LITEVER below.
<div class="popupcontainer flex hidden" id="inputboxcontainer"> <div class="popupcontainer flex hidden" id="inputboxcontainer">
<div class="popupbg flex"></div> <div class="popupbg flex"></div>
<div class="nspopup flexsize moderate"> <div class="nspopup flexsize higher">
<div class="popuptitlebar"> <div class="popuptitlebar">
<div class="popuptitletext" id="inputboxcontainertitle"></div> <div class="popuptitletext" id="inputboxcontainertitle"></div>
</div> </div>
@ -25282,7 +25624,7 @@ Current version indicated by LITEVER below.
<div class="popupcontainer flex hidden" id="msgboxcontainer"> <div class="popupcontainer flex hidden" id="msgboxcontainer">
<div class="popupbg flex"></div> <div class="popupbg flex"></div>
<div class="nspopup flexsizesmall moderate"> <div class="nspopup flexsizesmall higher">
<div class="popuptitlebar"> <div class="popuptitlebar">
<div class="popuptitletext" id="msgboxtitle"></div> <div class="popuptitletext" id="msgboxtitle"></div>
</div> </div>

View file

@ -59,7 +59,7 @@ dry_seq_break_max = 128
KcppVersion = "1.93.2" KcppVersion = "1.93.2"
showdebug = True showdebug = True
kcpp_instance = None #global running instance kcpp_instance = None #global running instance
global_memory = {"tunnel_url": "", "restart_target":"", "input_to_exit":False, "load_complete":False} global_memory = {"tunnel_url": "", "restart_target":"", "input_to_exit":False, "load_complete":False, "restart_override_config_target":""}
using_gui_launcher = False using_gui_launcher = False
handle = None handle = None
@ -3464,22 +3464,31 @@ Change Mode<br>
resp = {"success": False} resp = {"success": False}
if global_memory and args.admin and args.admindir and os.path.exists(args.admindir) and self.check_header_password(args.adminpassword): if global_memory and args.admin and args.admindir and os.path.exists(args.admindir) and self.check_header_password(args.adminpassword):
targetfile = "" targetfile = ""
overrideconfig = ""
try: try:
tempbody = json.loads(body) tempbody = json.loads(body)
if isinstance(tempbody, dict): if isinstance(tempbody, dict):
targetfile = tempbody.get('filename', "") targetfile = tempbody.get('filename', "")
overrideconfig = tempbody.get('overrideconfig', "")
except Exception: except Exception:
targetfile = "" targetfile = ""
if targetfile and targetfile!="": if targetfile and targetfile!="":
if targetfile=="unload_model": #special request to simply unload model if targetfile=="unload_model": #special request to simply unload model
print("Admin: Received request to unload model") print("Admin: Received request to unload model")
global_memory["restart_target"] = "unload_model" global_memory["restart_target"] = "unload_model"
global_memory["restart_override_config_target"] = ""
resp = {"success": True} resp = {"success": True}
else: else:
dirpath = os.path.abspath(args.admindir) dirpath = os.path.abspath(args.admindir)
targetfilepath = os.path.join(dirpath, targetfile) targetfilepath = os.path.join(dirpath, targetfile)
opts = [f for f in os.listdir(dirpath) if (f.lower().endswith(".kcpps") or f.lower().endswith(".kcppt") or f.lower().endswith(".gguf")) and os.path.isfile(os.path.join(dirpath, f))] opts = [f for f in os.listdir(dirpath) if (f.lower().endswith(".kcpps") or f.lower().endswith(".kcppt") or f.lower().endswith(".gguf")) and os.path.isfile(os.path.join(dirpath, f))]
if targetfile in opts and os.path.exists(targetfilepath): if targetfile in opts and os.path.exists(targetfilepath):
global_memory["restart_override_config_target"] = ""
if targetfile.lower().endswith(".gguf") and overrideconfig:
overrideconfigfilepath = os.path.join(dirpath, overrideconfig)
if overrideconfig and overrideconfig in opts and os.path.exists(overrideconfigfilepath):
print(f"Admin: Override config set to {overrideconfig}")
global_memory["restart_override_config_target"] = overrideconfig
print(f"Admin: Received request to reload config to {targetfile}") print(f"Admin: Received request to reload config to {targetfile}")
global_memory["restart_target"] = targetfile global_memory["restart_target"] = targetfile
resp = {"success": True} resp = {"success": True}
@ -4981,7 +4990,7 @@ def show_gui():
sdvaeitem2.grid() sdvaeitem2.grid()
sdvaeitem3.grid() sdvaeitem3.grid()
makecheckbox(images_tab, "Use TAE SD (AutoFix Broken VAE)", sd_vaeauto_var, 32,command=toggletaesd,tooltiptxt="Replace VAE with TAESD. May fix bad VAE.") makecheckbox(images_tab, "Use TAE SD (AutoFix Broken VAE)", sd_vaeauto_var, 32,command=toggletaesd,tooltiptxt="Replace VAE with TAESD. May fix bad VAE.")
makecheckbox(images_tab, "No VAE Tiling", sd_notile_var, 24,tooltiptxt="Disables VAE tiling, may not work for large images.") makecheckbox(images_tab, "No VAE Tiling", sd_notile_var, 34,tooltiptxt="Disables VAE tiling, may not work for large images.")
# audio tab # audio tab
audio_tab = tabcontent["Audio"] audio_tab = tabcontent["Audio"]
@ -6258,7 +6267,7 @@ def main(launch_args, default_args):
input() input()
else: # manager command queue for admin mode else: # manager command queue for admin mode
with multiprocessing.Manager() as mp_manager: with multiprocessing.Manager() as mp_manager:
global_memory = mp_manager.dict({"tunnel_url": "", "restart_target":"", "input_to_exit":False, "load_complete":False}) global_memory = mp_manager.dict({"tunnel_url": "", "restart_target":"", "input_to_exit":False, "load_complete":False, "restart_override_config_target":""})
if args.remotetunnel and not args.prompt and not args.benchmark and not args.cli: if args.remotetunnel and not args.prompt and not args.benchmark and not args.cli:
setuptunnel(global_memory, True if args.sdmodel else False) setuptunnel(global_memory, True if args.sdmodel else False)
@ -6275,6 +6284,7 @@ def main(launch_args, default_args):
while True: # keep the manager alive while True: # keep the manager alive
try: try:
restart_target = "" restart_target = ""
restart_override_config_target = ""
if not kcpp_instance or not kcpp_instance.is_alive(): if not kcpp_instance or not kcpp_instance.is_alive():
if fault_recovery_mode: if fault_recovery_mode:
#attempt to recover #attempt to recover
@ -6289,20 +6299,25 @@ def main(launch_args, default_args):
kcpp_instance.daemon = True kcpp_instance.daemon = True
kcpp_instance.start() kcpp_instance.start()
global_memory["restart_target"] = "" global_memory["restart_target"] = ""
global_memory["restart_override_config_target"] = ""
time.sleep(3) time.sleep(3)
else: else:
break # kill the program break # kill the program
if fault_recovery_mode and global_memory["load_complete"]: if fault_recovery_mode and global_memory["load_complete"]:
fault_recovery_mode = False fault_recovery_mode = False
restart_target = global_memory["restart_target"] restart_target = global_memory["restart_target"]
restart_override_config_target = global_memory["restart_override_config_target"]
if restart_target!="": if restart_target!="":
print(f"Reloading new model/config: {restart_target}") overridetxt = ("" if not restart_override_config_target else f" with override config {restart_override_config_target}")
print(f"Reloading new model/config: {restart_target}{overridetxt}")
global_memory["restart_target"] = "" global_memory["restart_target"] = ""
global_memory["restart_override_config_target"] = ""
time.sleep(0.5) #sleep for 0.5s then restart time.sleep(0.5) #sleep for 0.5s then restart
if args.admin and args.admindir: if args.admin and args.admindir:
dirpath = os.path.abspath(args.admindir) dirpath = os.path.abspath(args.admindir)
targetfilepath = os.path.join(dirpath, restart_target) targetfilepath = os.path.join(dirpath, restart_target)
if os.path.exists(targetfilepath) or restart_target=="unload_model": targetfilepath2 = os.path.join(dirpath, restart_override_config_target)
if (os.path.exists(targetfilepath) or restart_target=="unload_model") and (restart_override_config_target=="" or os.path.exists(targetfilepath2)):
print("Terminating old process...") print("Terminating old process...")
global_memory["load_complete"] = False global_memory["load_complete"] = False
kcpp_instance.terminate() kcpp_instance.terminate()
@ -6315,15 +6330,19 @@ def main(launch_args, default_args):
args.model_param = None args.model_param = None
args.model = None args.model = None
args.nomodel = True args.nomodel = True
elif targetfilepath.endswith(".gguf"): elif targetfilepath.endswith(".gguf") and restart_override_config_target=="":
reload_from_new_args(vars(default_args)) reload_from_new_args(vars(default_args))
args.model_param = targetfilepath args.model_param = targetfilepath
elif targetfilepath.endswith(".gguf") and restart_override_config_target!="":
reload_new_config(targetfilepath2)
args.model_param = targetfilepath
else: else:
reload_new_config(targetfilepath) reload_new_config(targetfilepath)
kcpp_instance = multiprocessing.Process(target=kcpp_main_process,kwargs={"launch_args": args, "g_memory": global_memory, "gui_launcher": False}) kcpp_instance = multiprocessing.Process(target=kcpp_main_process,kwargs={"launch_args": args, "g_memory": global_memory, "gui_launcher": False})
kcpp_instance.daemon = True kcpp_instance.daemon = True
kcpp_instance.start() kcpp_instance.start()
global_memory["restart_target"] = "" global_memory["restart_target"] = ""
global_memory["restart_override_config_target"] = ""
time.sleep(3) time.sleep(3)
else: else:
time.sleep(0.2) time.sleep(0.2)