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>
const LITEVER = 252;
const LITEVER = 253;
const urlParams = new URLSearchParams(window.location.search);
var localflag = urlParams.get('local'); //this will be replaced automatically in embedded kcpp
const STORAGE_PREFIX = (localflag?"e_":"")+"kaihordewebui_";
@ -2206,6 +2206,18 @@ Current version indicated by LITEVER below.
.color_pink {
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 {
background-color: #202020;
@ -3085,6 +3097,7 @@ Current version indicated by LITEVER below.
const HD_RES_PX = 768;
const VHD_RES_PX = 960;
const NO_HD_RES_PX = 512;
const PREVIEW_RES_PX = 200;
const AVATAR_PX = 384;
const SAVE_SLOTS = 10;
const num_regex_rows = 4;
@ -3367,6 +3380,7 @@ Current version indicated by LITEVER below.
websearch_multipass: false,
websearch_retain: false,
websearch_template: "",
wordsearch_enabled: false,
max_context_length: (localflag?4096:2048),
max_length: (localflag?512:256),
@ -11150,6 +11164,7 @@ Current version indicated by LITEVER below.
el.value = values[i];
dropdown.appendChild(el);
}
change_admin_config_selection();
document.getElementById("admincontainer").classList.remove("hidden");
}
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;
function trigger_admin_reload()
{
@ -11185,7 +11230,14 @@ Current version indicated by LITEVER below.
msgbox("No config file was selected.");
return;
}
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!="")
{
header['Authorization'] = 'Bearer ' + last_admin_key;
@ -11193,9 +11245,7 @@ Current version indicated by LITEVER below.
fetch(custom_kobold_endpoint + koboldcpp_admin_reload_endpoint, {
method: 'POST',
headers: header,
body: JSON.stringify({
"filename": targetfile
})
body: JSON.stringify(payload)
})
.then(x => x.json())
.then(values => {
@ -12353,6 +12403,7 @@ Current version indicated by LITEVER below.
document.getElementById("run_in_background").checked = run_in_background;
document.getElementById("auto_ctxlen").checked = localsettings.auto_ctxlen;
document.getElementById("auto_genamt").checked = localsettings.auto_genamt;
document.getElementById("wordsearch_toggle").checked = localsettings.wordsearch_enabled;
if(localflag)
{
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.auto_ctxlen = (document.getElementById("auto_ctxlen").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_negprompt = document.getElementById("negpromptinput").value;
@ -13240,9 +13292,20 @@ Current version indicated by LITEVER below.
function toggle_uistyle()
{
//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'); }
else { document.getElementById('btn_aesthetics').classList.add('hidden'); }
if (document.getElementById('gui_type').value == 2) {
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);
if (document.getElementById('gui_type').value == 0) {
document.getElementById('classicmodeoptions').classList.remove('hidden');
}
else {
document.getElementById('classicmodeoptions').classList.add('hidden');
}
}
function select_welcome_ui()
@ -13345,8 +13408,12 @@ Current version indicated by LITEVER below.
document.getElementById('gui_type').value = 0;
}
if (document.getElementById('gui_type').value==2) { document.getElementById('btn_aesthetics').classList.remove('hidden'); }
else { document.getElementById('btn_aesthetics').classList.add('hidden'); }
if (document.getElementById('gui_type').value == 2) {
document.getElementById('btn_aesthetics').classList.remove('hidden');
}
else {
document.getElementById('btn_aesthetics').classList.add('hidden');
}
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()
{
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) { }
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",()=>{
let userinput = getInputBoxValue().trim();
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)
{
input = input.replace(/<\s*\/?\s*\w+\s*[^>]*>/gi, ""); //replace html tags
input = input.replace(/</g, ""); // Remove any remaining `<` characters
input = input.replace(/(?:javascript|data|vbscript|file):/gi, "");
input = input.replace(/<\s*\/?\s*\w+\s*[^>]*>/gi, "");
input = input.replace(/</g, "");
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;
}
function apply_custom_css()
{
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());
indexeddb_save("savedcustomcss", userinput);
let styleElement = document.getElementById('custom_css');
@ -13783,6 +13885,8 @@ Current version indicated by LITEVER below.
document.getElementById("input_text").value = "";
document.getElementById("corpo_cht_inp").value = "";
document.getElementById("cht_inp").value = "";
let wsresultsContainer = document.getElementById("wordsearch_results");
if(wsresultsContainer){wsresultsContainer.innerHTML="";}
chat_resize_input();
image_db = {};
interrogation_db = {};
@ -17378,7 +17482,7 @@ Current version indicated by LITEVER below.
}
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;
dimH = dim;
let reinvertcolor = localsettings.invert_colors?" invert_colors":"";
@ -19792,6 +19896,14 @@ Current version indicated by LITEVER below.
{
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()
@ -21702,7 +21814,7 @@ Current version indicated by LITEVER below.
}
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`; }
@ -22005,6 +22117,11 @@ Current version indicated by LITEVER below.
`<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;}
.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>
`;
@ -22167,7 +22284,7 @@ Current version indicated by LITEVER below.
});
}
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];
@ -22194,7 +22311,7 @@ Current version indicated by LITEVER below.
}
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)
{
@ -22654,6 +22771,209 @@ Current version indicated by LITEVER below.
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>
</head>
@ -22770,6 +23090,20 @@ Current version indicated by LITEVER below.
<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>
</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>
@ -22883,6 +23217,8 @@ Current version indicated by LITEVER below.
</div>
</div>
</div>
</div>
<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>
</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>
<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>
</div>
@ -24925,7 +25265,9 @@ Current version indicated by LITEVER below.
<div>
<b class="color_white" style="padding: 5px;">Change Loaded Model / Config:</b><br>
<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>
<button type="button" style="margin-left:2px;width:146px" class="btn btn-primary" onclick="trigger_admin_reload()">Reload KoboldCpp</button>
</div>
@ -25246,7 +25588,7 @@ Current version indicated by LITEVER below.
<div class="popupcontainer flex hidden" id="inputboxcontainer">
<div class="popupbg flex"></div>
<div class="nspopup flexsize moderate">
<div class="nspopup flexsize higher">
<div class="popuptitlebar">
<div class="popuptitletext" id="inputboxcontainertitle"></div>
</div>
@ -25282,7 +25624,7 @@ Current version indicated by LITEVER below.
<div class="popupcontainer flex hidden" id="msgboxcontainer">
<div class="popupbg flex"></div>
<div class="nspopup flexsizesmall moderate">
<div class="nspopup flexsizesmall higher">
<div class="popuptitlebar">
<div class="popuptitletext" id="msgboxtitle"></div>
</div>

View file

@ -59,7 +59,7 @@ dry_seq_break_max = 128
KcppVersion = "1.93.2"
showdebug = True
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
handle = None
@ -3464,22 +3464,31 @@ Change Mode<br>
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):
targetfile = ""
overrideconfig = ""
try:
tempbody = json.loads(body)
if isinstance(tempbody, dict):
targetfile = tempbody.get('filename', "")
overrideconfig = tempbody.get('overrideconfig', "")
except Exception:
targetfile = ""
if targetfile and targetfile!="":
if targetfile=="unload_model": #special request to simply unload model
print("Admin: Received request to unload model")
global_memory["restart_target"] = "unload_model"
global_memory["restart_override_config_target"] = ""
resp = {"success": True}
else:
dirpath = os.path.abspath(args.admindir)
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))]
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}")
global_memory["restart_target"] = targetfile
resp = {"success": True}
@ -4981,7 +4990,7 @@ def show_gui():
sdvaeitem2.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, "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 = tabcontent["Audio"]
@ -6258,7 +6267,7 @@ def main(launch_args, default_args):
input()
else: # manager command queue for admin mode
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:
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
try:
restart_target = ""
restart_override_config_target = ""
if not kcpp_instance or not kcpp_instance.is_alive():
if fault_recovery_mode:
#attempt to recover
@ -6289,20 +6299,25 @@ def main(launch_args, default_args):
kcpp_instance.daemon = True
kcpp_instance.start()
global_memory["restart_target"] = ""
global_memory["restart_override_config_target"] = ""
time.sleep(3)
else:
break # kill the program
if fault_recovery_mode and global_memory["load_complete"]:
fault_recovery_mode = False
restart_target = global_memory["restart_target"]
restart_override_config_target = global_memory["restart_override_config_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_override_config_target"] = ""
time.sleep(0.5) #sleep for 0.5s then restart
if args.admin and args.admindir:
dirpath = os.path.abspath(args.admindir)
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...")
global_memory["load_complete"] = False
kcpp_instance.terminate()
@ -6315,15 +6330,19 @@ def main(launch_args, default_args):
args.model_param = None
args.model = None
args.nomodel = True
elif targetfilepath.endswith(".gguf"):
elif targetfilepath.endswith(".gguf") and restart_override_config_target=="":
reload_from_new_args(vars(default_args))
args.model_param = targetfilepath
elif targetfilepath.endswith(".gguf") and restart_override_config_target!="":
reload_new_config(targetfilepath2)
args.model_param = targetfilepath
else:
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.daemon = True
kcpp_instance.start()
global_memory["restart_target"] = ""
global_memory["restart_override_config_target"] = ""
time.sleep(3)
else:
time.sleep(0.2)