mirror of
https://github.com/LostRuins/koboldcpp.git
synced 2025-09-10 17:14:36 +00:00
added support for server side save slots
This commit is contained in:
parent
5ee7cbe08c
commit
ccd2dbe020
3 changed files with 705 additions and 96 deletions
217
kcpp_docs.embd
217
kcpp_docs.embd
|
@ -1491,6 +1491,223 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/api/extra/data/save": {
|
||||
"post": {
|
||||
"description": "Saves data to a slot in a database file in the KoboldCpp server.",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"slot": "1",
|
||||
"format": "kcpp_lzma_b64",
|
||||
"title": "Untitled Story",
|
||||
"data": "base64_data",
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"slot": {
|
||||
"type": "string",
|
||||
"description": "Save slot id to save to."
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"description": "Save format, not used currently"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Title of this saved file"
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"description": "Save data text string"
|
||||
},
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"success": true,
|
||||
"error": ""
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the operation was successful."
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "What went wrong."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful request"
|
||||
}
|
||||
},
|
||||
"summary": "Saves data to a slot in a database file in the KoboldCpp server.",
|
||||
"tags": [
|
||||
"api/extra/data"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/extra/data/load": {
|
||||
"post": {
|
||||
"description": "Loads data from a save slot in the database file in the KoboldCpp server.",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"slot": "1",
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"slot": {
|
||||
"type": "string",
|
||||
"description": "Save slot id"
|
||||
},
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"success": true,
|
||||
"data": "base64_data"
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the operation was successful."
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"description": "Text string containing the loaded data from server."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful request"
|
||||
}
|
||||
},
|
||||
"summary": "Loads data from a save slot in the database file in the KoboldCpp server.",
|
||||
"tags": [
|
||||
"api/extra/data"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/extra/data/list": {
|
||||
"post": {
|
||||
"description": "List available saved slots from the KoboldCpp server, returns an array of strings containing their titles.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": ["Saved Story 1", "Saved Story 2", "", ""],
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful request"
|
||||
}
|
||||
},
|
||||
"summary": "List available saved slots from the KoboldCpp server.",
|
||||
"tags": [
|
||||
"api/extra/data"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/admin/list_options": {
|
||||
"get": {
|
||||
"summary": "List available .kcpps files to load.",
|
||||
"description": "List available .kcpps files to load.",
|
||||
"tags": [
|
||||
"api/admin"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": ["file1.kcpps","file2.kcpps"],
|
||||
"schema": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful request"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"/api/admin/reload_config": {
|
||||
"post": {
|
||||
"description": "Switches the loaded config, along with any settings and model file changes.",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"filename": "file1.kcpps",
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"description": "Filename of the .kcpps config to be loaded. Any associated files and models will be automatically swapped in."
|
||||
},
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"success": true
|
||||
},
|
||||
"schema": {
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the operation was successful."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Successful request"
|
||||
}
|
||||
},
|
||||
"summary": "Switches the currently loaded .kcpps config, and reloads any changed files or models.",
|
||||
"tags": [
|
||||
"api/admin"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/props": {
|
||||
"get": {
|
||||
"summary": "Returns the Jinja template stored in the GGUF model, if found.",
|
||||
|
|
436
klite.embd
436
klite.embd
|
@ -12,7 +12,7 @@ Current version indicated by LITEVER below.
|
|||
-->
|
||||
|
||||
<script>
|
||||
const LITEVER = 217;
|
||||
const LITEVER = 219;
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
var localflag = true;
|
||||
const STORAGE_PREFIX = (localflag?"e_":"")+"kaihordewebui_";
|
||||
|
@ -462,13 +462,13 @@ Current version indicated by LITEVER below.
|
|||
.saveloadpopup {
|
||||
width: 660px;
|
||||
background-color: #262626;
|
||||
margin-top: 80px;
|
||||
margin-top: 120px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.saveloadpopup {
|
||||
width: 100%;
|
||||
background-color: #262626;
|
||||
margin-top: 80px;
|
||||
margin-top: 120px;
|
||||
}
|
||||
}
|
||||
.saveloadgrid
|
||||
|
@ -478,7 +478,7 @@ Current version indicated by LITEVER below.
|
|||
margin-top: 4px;
|
||||
padding: 4px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
gap: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
@media (max-width: 340px) {
|
||||
|
@ -2212,6 +2212,24 @@ Current version indicated by LITEVER below.
|
|||
.bg_teal:disabled:hover {
|
||||
background-color: #8a8a8a;
|
||||
}
|
||||
.bg_primary {
|
||||
background-color: #337ab7;
|
||||
}
|
||||
.bg_primary:hover {
|
||||
background-color: #286090;
|
||||
}
|
||||
.bg_primary:active:focus {
|
||||
background-color: #23527c;
|
||||
}
|
||||
.bg_primary:focus {
|
||||
background-color: #23527c;
|
||||
}
|
||||
.bg_primary:disabled {
|
||||
background-color: #8a8a8a;
|
||||
}
|
||||
.bg_primary:disabled:hover {
|
||||
background-color: #8a8a8a;
|
||||
}
|
||||
|
||||
.bluebtn {
|
||||
color: #fff;
|
||||
|
@ -2832,6 +2850,9 @@ Current version indicated by LITEVER below.
|
|||
const koboldcpp_tts_endpoint = "/api/extra/tts";
|
||||
const koboldcpp_admin_list_endpoint = "/api/admin/list_options";
|
||||
const koboldcpp_admin_reload_endpoint = "/api/admin/reload_config";
|
||||
const koboldcpp_savedata_list_endpoint = "/api/extra/data/list";
|
||||
const koboldcpp_savedata_save_endpoint = "/api/extra/data/save";
|
||||
const koboldcpp_savedata_load_endpoint = "/api/extra/data/load";
|
||||
|
||||
const oai_models_endpoint = "/models";
|
||||
const oai_submit_endpoint = "/completions";
|
||||
|
@ -2895,7 +2916,7 @@ Current version indicated by LITEVER below.
|
|||
const HD_RES_PX = 768;
|
||||
const NO_HD_RES_PX = 512;
|
||||
const AVATAR_PX = 384;
|
||||
const SAVE_SLOTS = 6;
|
||||
const SAVE_SLOTS = 8;
|
||||
const num_regex_rows = 4;
|
||||
const default_websearch_template = `### New Task:\nFrom above text, rephrase the search engine query "{{QUERY}}" as a single short phrase (for search engines) using proper nouns, references and names to avoid ambiguity.\n\n### Rephrased Search Query Created:\n`;
|
||||
|
||||
|
@ -2983,6 +3004,7 @@ Current version indicated by LITEVER below.
|
|||
var koboldcpp_has_vision = false;
|
||||
var koboldcpp_has_multiplayer = false;
|
||||
var koboldcpp_has_websearch = false;
|
||||
var koboldcpp_has_savedatafile = false;
|
||||
var koboldcpp_admin_type = 0; //0 = no admin, 1=has admin, 2=protected admin
|
||||
var lastSearchQuery = "";
|
||||
var lastSearchResults = [];
|
||||
|
@ -6066,6 +6088,10 @@ Current version indicated by LITEVER below.
|
|||
{
|
||||
return (custom_kobold_endpoint!="" && koboldcpp_version && koboldcpp_version!="" && compare_version_str(koboldcpp_version, "1.78") >= 0 && koboldcpp_has_multiplayer);
|
||||
}
|
||||
function is_using_kcpp_with_savedatafile()
|
||||
{
|
||||
return (custom_kobold_endpoint!="" && koboldcpp_version && koboldcpp_version!="" && compare_version_str(koboldcpp_version, "1.84") >= 0 && koboldcpp_has_savedatafile);
|
||||
}
|
||||
function is_using_kcpp_with_websearch()
|
||||
{
|
||||
return (custom_kobold_endpoint!="" && koboldcpp_version && koboldcpp_version!="" && compare_version_str(koboldcpp_version, "1.80") >= 0 && koboldcpp_has_websearch);
|
||||
|
@ -9513,6 +9539,7 @@ Current version indicated by LITEVER below.
|
|||
koboldcpp_has_websearch = (data.websearch?true:false);
|
||||
koboldcpp_has_tts = (data.tts?true:false);
|
||||
koboldcpp_admin_type = (data.admin?data.admin:0);
|
||||
koboldcpp_has_savedatafile = (data.savedata?true:false)
|
||||
let has_password = (data.protected?true:false);
|
||||
let has_txt2img = (data.txt2img?true:false);
|
||||
let no_txt_model = (mdlname=="inactive");
|
||||
|
@ -9992,11 +10019,68 @@ Current version indicated by LITEVER below.
|
|||
});
|
||||
}
|
||||
|
||||
var cachedsaveslotlabels = [];
|
||||
var netsaveslotlabels = [];
|
||||
function saveloadchangeslot(updatelist=false)
|
||||
{
|
||||
let selectedslot = document.getElementById("saveslotselecteddropdown").value;
|
||||
let selectedlocation = document.getElementById("saveslotlocationdropdown").value;
|
||||
let selectedslotlabel = null;
|
||||
let choices = "";
|
||||
let foundoption = false;
|
||||
if(selectedlocation=="1") //local
|
||||
{
|
||||
for(let i=0;i<cachedsaveslotlabels.length;++i)
|
||||
{
|
||||
let testslot = cachedsaveslotlabels[i];
|
||||
if(selectedslot==i)
|
||||
{
|
||||
selectedslotlabel = testslot;
|
||||
foundoption = true;
|
||||
}
|
||||
let lbl = (i+1);
|
||||
let tsdesc = (testslot.length>50)?testslot.substring(0,50)+"...":testslot;
|
||||
let slotname = (testslot?`Local Slot `+(lbl)+` - `+tsdesc+``:`Local Slot `+(lbl)+` - [ Empty ]`);
|
||||
choices += `<option value=${i}${(selectedslot==i)?" selected":""}>${slotname}</option>`;
|
||||
}
|
||||
} else if(selectedlocation=="2") { //remote saves
|
||||
for(let i=0;i<netsaveslotlabels.length;++i)
|
||||
{
|
||||
let testslot = netsaveslotlabels[i];
|
||||
if(selectedslot==i)
|
||||
{
|
||||
selectedslotlabel = testslot;
|
||||
foundoption = true;
|
||||
}
|
||||
let lbl = (i+1);
|
||||
let tsdesc = (testslot.length>50)?testslot.substring(0,50)+"...":testslot;
|
||||
let slotname = (testslot?`Server Slot `+(lbl)+` - `+tsdesc+``:`Server Slot `+(lbl)+` - [ Empty ]`);
|
||||
choices += `<option value=${i}${(selectedslot==i)?" selected":""}>${slotname}</option>`;
|
||||
}
|
||||
}
|
||||
document.getElementById("loadfromslot").disabled = (selectedslotlabel?false:true);
|
||||
document.getElementById("downloadslot").disabled = (selectedslotlabel?false:true);
|
||||
document.getElementById("deleteslot").disabled = (selectedslotlabel?false:true);
|
||||
if(!foundoption)
|
||||
{
|
||||
document.getElementById("saveslotselecteddropdown").selectedIndex = 0;
|
||||
}
|
||||
if(updatelist)
|
||||
{
|
||||
document.getElementById("saveslotselecteddropdown").innerHTML = choices;
|
||||
}
|
||||
}
|
||||
function display_saveloadcontainer()
|
||||
{
|
||||
mainmenu_untab(true);
|
||||
document.getElementById("saveloadcontainer").classList.remove("hidden");
|
||||
|
||||
if (is_using_kcpp_with_savedatafile()) {
|
||||
document.getElementById("kcppsaveavailable").classList.remove("hidden");
|
||||
} else {
|
||||
document.getElementById("kcppsaveavailable").classList.add("hidden");
|
||||
}
|
||||
|
||||
let slotpromises = [];
|
||||
for(let i=0;i<SAVE_SLOTS;++i)
|
||||
{
|
||||
|
@ -10004,111 +10088,277 @@ Current version indicated by LITEVER below.
|
|||
}
|
||||
Promise.all(slotpromises).then(slotlabels=>
|
||||
{
|
||||
let filetable = ``;
|
||||
let entry = `<div style="display:flex">
|
||||
<button type="button" style="font-size:12px; margin:2px;width:33%" name="localsave" class="btn btn-primary" onclick="hide_popups();save_file_button()">`+"💾<br>Download File"+`</button>
|
||||
<button type="button" style="font-size:12px; margin:2px;width:33%" name="localload" class="btn btn-primary" onclick="hide_popups();load_file_button()">`+"📁<br>Open File"+`</button>
|
||||
<button type="button" style="font-size:12px; margin:2px;width:34%" name="shareurl" class="btn btn-primary" onclick="hide_popups();share_story_button()">`+"🌐<br>Share"+`</button>
|
||||
</div>
|
||||
<div style="margin-top:3px; text-align: center; align-self: center; width: calc(100% - 184px);">
|
||||
<span style="font-weight:bold;text-decoration: underline;">Temporary Browser Storage</span>
|
||||
</div>`;
|
||||
filetable += entry;
|
||||
|
||||
for(let i=0;i<slotlabels.length;++i)
|
||||
{
|
||||
let testslot = slotlabels[i];
|
||||
let lbl = (i+1);
|
||||
entry = `<div style="display:flex; height:42px;">
|
||||
<div style="margin:3px; text-align: center; align-self: center; width: calc(100% - 184px);">
|
||||
`+(testslot?`[ Slot `+(lbl)+` - `+testslot+` ]`:`[ Slot `+(lbl)+` - Empty ]`)+`
|
||||
</div>
|
||||
<div style="text-align: right; align-self: center; width: 184px;">
|
||||
<button type="button" title="Save To Slot ${lbl}" class="btn btn-primary" onclick="save_to_slot(${i})"><img class="btnicon-save"/></button>
|
||||
<button type="button" title="Load From Slot ${lbl}" class="btn btn-primary" onclick="load_from_slot(${i})" `+(testslot?"":"disabled")+`><img class="btnicon-load"/></button>
|
||||
<button type="button" title="Download Slot ${lbl}" class="btn btn-primary bg_green" onclick="download_from_slot(${i})" `+(testslot?"":"disabled")+`><img class="btnicon-download"/></button>
|
||||
<button type="button" title="Delete Slot ${lbl}" class="btn btn-primary bg_red" onclick="delete_from_slot(${i})" `+(testslot?"":"disabled")+`><img class="btnicon-delete"/></button>
|
||||
</div></div>`;
|
||||
filetable += entry;
|
||||
}
|
||||
cachedsaveslotlabels = slotlabels;
|
||||
saveloadchangeslot(true);
|
||||
populate_corpo_leftpanel();
|
||||
document.getElementById("saveloadentries").innerHTML = filetable;
|
||||
});
|
||||
if(is_using_kcpp_with_savedatafile())
|
||||
{
|
||||
//grab saves
|
||||
fetch(custom_kobold_endpoint + koboldcpp_savedata_list_endpoint, {
|
||||
method: 'POST', // or 'PUT'
|
||||
headers: get_kobold_header(),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
netsaveslotlabels = data;
|
||||
saveloadchangeslot(true);
|
||||
populate_corpo_leftpanel();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
function save_to_slot(slot)
|
||||
function save_to_slot(slot,islocal)
|
||||
{
|
||||
let defaultsavename = (localsettings.opmode==1?"Untitled Story":(localsettings.opmode==2?"Untitled Adventure":(localsettings.opmode==3?"Untitled Chat":"Untitled Instruct")));
|
||||
let savename = defaultsavename + " " + new Date().toLocaleString();
|
||||
let slotnumshown = (slot+1);
|
||||
indexeddb_load("slot_"+slot+"_meta","").then(testslot=>{
|
||||
let slotnumshown = (parseInt(slot)+1);
|
||||
let newcompressedstory = generate_compressed_story(true, true, true);
|
||||
if(islocal)
|
||||
{
|
||||
indexeddb_load("slot_"+slot+"_meta","").then(testslot=>{
|
||||
if (testslot) {
|
||||
savename = testslot;
|
||||
}
|
||||
const slotwrite = function () {
|
||||
warn_on_quit = false;
|
||||
inputBox("Enter a label for this Browser Storage Slot data", "Enter a label", savename, defaultsavename, () => {
|
||||
let userinput = getInputBoxValue();
|
||||
if (userinput.trim() == "") {
|
||||
userinput = defaultsavename;
|
||||
}
|
||||
indexeddb_save("slot_" + slot + "_data", newcompressedstory)
|
||||
indexeddb_save("slot_" + slot + "_meta", userinput).then(()=>{display_saveloadcontainer()});
|
||||
});
|
||||
}
|
||||
|
||||
if (testslot) {
|
||||
msgboxYesNo("Overwrite existing story in Browser Storage Slot " + slotnumshown + "?", "Overwrite Browser Storage Slot " + slotnumshown, () => {
|
||||
slotwrite();
|
||||
}, null);
|
||||
}
|
||||
else {
|
||||
slotwrite();
|
||||
}
|
||||
});
|
||||
} else { //remote save
|
||||
let testslot = (slot<netsaveslotlabels.length?netsaveslotlabels[slot]:"");
|
||||
if (testslot) {
|
||||
savename = testslot;
|
||||
}
|
||||
let newcompressedstory = generate_compressed_story(true, true, true);
|
||||
|
||||
const slotwrite = function () {
|
||||
warn_on_quit = false;
|
||||
inputBox("Enter a label for this Browser Storage Slot data", "Enter a label", savename, defaultsavename, () => {
|
||||
inputBox("Enter a label for this Server Storage Slot data", "Enter a label", savename, defaultsavename, () => {
|
||||
let userinput = getInputBoxValue();
|
||||
if (userinput.trim() == "") {
|
||||
userinput = defaultsavename;
|
||||
}
|
||||
indexeddb_save("slot_" + slot + "_data", newcompressedstory)
|
||||
indexeddb_save("slot_" + slot + "_meta", userinput).then(()=>{display_saveloadcontainer()});
|
||||
//complete remote save
|
||||
fetch(custom_kobold_endpoint + koboldcpp_savedata_save_endpoint, {
|
||||
method: 'POST', // or 'PUT'
|
||||
headers: get_kobold_header(),
|
||||
body: JSON.stringify({
|
||||
"slot": slot,
|
||||
"format": "kcpp_lzma_b64",
|
||||
"title": userinput,
|
||||
"data": newcompressedstory,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if(!data.success)
|
||||
{
|
||||
msgbox(`Save Error: ${data.error}`,"Save Failed",false,false,()=>{
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
display_saveloadcontainer();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
msgbox(`Save Error: ${error}`,"Save Failed",false,false,()=>{
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (testslot) {
|
||||
msgboxYesNo("Overwrite existing story in Browser Storage Slot " + slotnumshown + "?", "Overwrite Storage Slot " + slotnumshown, () => {
|
||||
msgboxYesNo("Overwrite existing story in Server Storage Slot " + slotnumshown + "?", "Overwrite Server Storage Slot " + slotnumshown, () => {
|
||||
slotwrite();
|
||||
}, null);
|
||||
}
|
||||
else {
|
||||
slotwrite();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function load_from_slot(slot)
|
||||
function load_from_slot(slot,islocal)
|
||||
{
|
||||
indexeddb_load("slot_"+slot+"_data","").then(loadedstorycompressed=>{
|
||||
if(loadedstorycompressed)
|
||||
{
|
||||
hide_popups();
|
||||
import_compressed_story(loadedstorycompressed,false);
|
||||
}else{
|
||||
msgbox("Unable to load story from browser storage","Browser Storage Load Failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
function download_from_slot(slot)
|
||||
{
|
||||
indexeddb_load("slot_"+slot+"_data","").then(loadedstorycompressed=>{
|
||||
if(loadedstorycompressed)
|
||||
{
|
||||
tempfileobj = decompress_story(loadedstorycompressed);
|
||||
if(tempfileobj)
|
||||
if(islocal)
|
||||
{
|
||||
indexeddb_load("slot_"+slot+"_data","").then(loadedstorycompressed=>{
|
||||
if(loadedstorycompressed)
|
||||
{
|
||||
save_file_button(true);
|
||||
hide_popups();
|
||||
import_compressed_story(loadedstorycompressed,false);
|
||||
}else{
|
||||
msgbox("Unable to load story from browser storage","Browser Storage Load Failed");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fetch(custom_kobold_endpoint + koboldcpp_savedata_load_endpoint, {
|
||||
method: 'POST', // or 'PUT'
|
||||
headers: get_kobold_header(),
|
||||
body: JSON.stringify({
|
||||
"slot": slot,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((resp) => {
|
||||
if(!resp.success || !resp.data)
|
||||
{
|
||||
msgbox("Error: Unable to load story from server storage","Server Storage Load Failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
tempfileobj = generate_base_storyobj();
|
||||
msgbox("Story could not be downloaded. Try loading it first.","Browser Storage Load Failed");
|
||||
hide_popups();
|
||||
import_compressed_story(resp.data.data,false);
|
||||
}
|
||||
}else{
|
||||
msgbox("Unable to load story from browser storage","Browser Storage Load Failed");
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
msgbox("Error: Unable to load story from server storage","Server Storage Load Failed");
|
||||
});
|
||||
}
|
||||
}
|
||||
function delete_from_slot(slot)
|
||||
function download_from_slot(slot,islocal)
|
||||
{
|
||||
let slotnumshown = (slot+1);
|
||||
msgboxYesNo("Delete story in Browser Storage Slot "+slotnumshown+"?","Delete Storage Slot "+slotnumshown,()=>{
|
||||
indexeddb_save("slot_"+slot+"_data", "");
|
||||
indexeddb_save("slot_"+slot+"_meta", "").then(()=>{display_saveloadcontainer()});
|
||||
},()=>{
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
let ondl = function (loadedstorycompressed) {
|
||||
if (loadedstorycompressed) {
|
||||
tempfileobj = decompress_story(loadedstorycompressed);
|
||||
if (tempfileobj) {
|
||||
save_file_button(true);
|
||||
}
|
||||
else {
|
||||
tempfileobj = generate_base_storyobj();
|
||||
msgbox("Story could not be downloaded. Try loading it first.", "Download Failed");
|
||||
}
|
||||
} else {
|
||||
msgbox("Unable to download story.", "Download Load Failed");
|
||||
}
|
||||
};
|
||||
|
||||
if(islocal)
|
||||
{
|
||||
indexeddb_load("slot_"+slot+"_data","").then(loadedstorycompressed=>{
|
||||
ondl(loadedstorycompressed);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch(custom_kobold_endpoint + koboldcpp_savedata_load_endpoint, {
|
||||
method: 'POST', // or 'PUT'
|
||||
headers: get_kobold_header(),
|
||||
body: JSON.stringify({
|
||||
"slot": slot,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((resp) => {
|
||||
if(!resp.success || !resp.data)
|
||||
{
|
||||
msgbox("Error: Unable to load story from server storage","Server Storage Load Failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
ondl(resp.data.data);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
msgbox("Error: Unable to load story from server storage","Server Storage Load Failed");
|
||||
});
|
||||
}
|
||||
}
|
||||
function delete_from_slot(slot,islocal)
|
||||
{
|
||||
let slotnumshown = (parseInt(slot)+1);
|
||||
if (islocal) {
|
||||
msgboxYesNo("Delete story in Browser Storage Slot " + slotnumshown + "?", "Delete Browser Storage Slot " + slotnumshown, () => {
|
||||
indexeddb_save("slot_" + slot + "_data", "");
|
||||
indexeddb_save("slot_" + slot + "_meta", "").then(() => { display_saveloadcontainer() });
|
||||
}, () => {
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
} else {
|
||||
msgboxYesNo("Delete story in Server Storage Slot " + slotnumshown + "?", "Delete Server Storage Slot " + slotnumshown, () => {
|
||||
fetch(custom_kobold_endpoint + koboldcpp_savedata_save_endpoint, {
|
||||
method: 'POST', // or 'PUT'
|
||||
headers: get_kobold_header(),
|
||||
body: JSON.stringify({
|
||||
"slot": slot,
|
||||
"format": "",
|
||||
"title": "",
|
||||
"data": "",
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if(!data.success)
|
||||
{
|
||||
msgbox(`Delete Error: ${data.error}`,"Delete Failed",false,false,()=>{
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
display_saveloadcontainer();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
msgbox(`Delete Error: ${error}`,"Delete Failed",false,false,()=>{
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
});
|
||||
}, () => {
|
||||
display_saveloadcontainer();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function save_to_curr_slot()
|
||||
{
|
||||
let selectedslot = document.getElementById("saveslotselecteddropdown").value;
|
||||
let selectedlocation = document.getElementById("saveslotlocationdropdown").value;
|
||||
let islocal = (selectedlocation=="1");
|
||||
save_to_slot(selectedslot,islocal);
|
||||
}
|
||||
function load_from_curr_slot()
|
||||
{
|
||||
let selectedslot = document.getElementById("saveslotselecteddropdown").value;
|
||||
let selectedlocation = document.getElementById("saveslotlocationdropdown").value;
|
||||
let islocal = (selectedlocation=="1");
|
||||
load_from_slot(selectedslot,islocal);
|
||||
}
|
||||
function download_from_curr_slot()
|
||||
{
|
||||
let selectedslot = document.getElementById("saveslotselecteddropdown").value;
|
||||
let selectedlocation = document.getElementById("saveslotlocationdropdown").value;
|
||||
let islocal = (selectedlocation=="1");
|
||||
download_from_slot(selectedslot,islocal);
|
||||
}
|
||||
function delete_from_curr_slot()
|
||||
{
|
||||
let selectedslot = document.getElementById("saveslotselecteddropdown").value;
|
||||
let selectedlocation = document.getElementById("saveslotlocationdropdown").value;
|
||||
let islocal = (selectedlocation=="1");
|
||||
delete_from_slot(selectedslot,islocal);
|
||||
}
|
||||
|
||||
var cached_model_list = null;
|
||||
|
@ -20213,8 +20463,38 @@ Current version indicated by LITEVER below.
|
|||
|
||||
<div style="overflow: auto;">
|
||||
<div id="saveloadentries" class="menutext saveloadgrid">
|
||||
<div style="display:flex">
|
||||
<button type="button" style="font-size:12px; margin:2px;width:33%" name="localsave" class="btn btn-primary" onclick="hide_popups();save_file_button()">💾<br>Download File</button>
|
||||
<button type="button" style="font-size:12px; margin:2px;width:33%" name="localload" class="btn btn-primary" onclick="hide_popups();load_file_button()">📁<br>Open File</button>
|
||||
<button type="button" style="font-size:12px; margin:2px;width:34%" name="shareurl" class="btn btn-primary" onclick="hide_popups();share_story_button()">🌐<br>Share</button>
|
||||
</div>
|
||||
<div style="margin-top:3px; text-align: center; align-self: center; width: 100%">
|
||||
<span style="font-weight:bold;text-decoration: underline;">Slot Storage Option</span>
|
||||
</div>
|
||||
<div style="display:flex;">
|
||||
<div style="width: 92px; margin:8px; margin-left: 4px; margin-right: 4px; font-size: 14px;">Data Location</div>
|
||||
<div style="margin:3px; text-align: center; align-self: center; width: calc(100% - 94px);">
|
||||
<select title="Select Slot Location" style="padding:4px;" class="form-control" id="saveslotlocationdropdown" onchange="saveloadchangeslot(true)">
|
||||
<option value="1">Local Browser Cache</option>
|
||||
<option value="2" id="kcppsaveavailable" class="hidden">KoboldCpp Server Storage</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;">
|
||||
<div style="width: 92px; margin:8px; margin-left: 4px; margin-right: 4px; font-size: 14px;">Selected Slot</div>
|
||||
<div style="margin:3px; text-align: center; align-self: center; width: calc(100% - 94px);">
|
||||
<select title="Select Save Slot" style="padding:4px;" class="form-control" id="saveslotselecteddropdown" onchange="saveloadchangeslot(false)">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width:100%;align-self: center;">
|
||||
<button type="button" id="savetoslot" title="Save To Slot" class="btn btn-primary bg_primary" onclick="save_to_curr_slot()"><img class="btnicon-save"/> Save Slot</button>
|
||||
<button type="button" id="loadfromslot" title="Load From Slot" class="btn btn-primary bg_primary" onclick="load_from_curr_slot()"><img class="btnicon-load"/> Load Slot</button>
|
||||
<button type="button" id="downloadslot" title="Download Slot" class="btn btn-primary bg_green" onclick="download_from_curr_slot()"><img class="btnicon-download"/> Download</button>
|
||||
<button type="button" id="deleteslot" title="Delete Slot" class="btn btn-primary bg_red" onclick="delete_from_curr_slot()"><img class="btnicon-delete"/> Delete Slot</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menutext"><p style="padding:6px;font-size: 10px;" class="color_red">Caution: Storage Slots are saved to a temporary cache and can be deleted by your browser. To avoid losing data, use the download file button.</p></div>
|
||||
<div class="menutext"><p style="padding:6px;font-size: 10px;" class="color_red">Caution: Local Storage Slots are saved to a temporary cache and can be deleted by your browser. KoboldCpp remote storage will save data to a file in your KoboldCpp server. To avoid losing data, use the download file button.</p></div>
|
||||
<div class="popupfooter">
|
||||
<button type="button" class="btn btn-primary" id=""
|
||||
onclick="hide_popups()">Back</button>
|
||||
|
|
148
koboldcpp.py
148
koboldcpp.py
|
@ -40,6 +40,7 @@ logprobs_max = 5
|
|||
default_draft_amount = 8
|
||||
default_ttsmaxlen = 4096
|
||||
default_visionmaxres = 1024
|
||||
net_save_slots = 8
|
||||
|
||||
# abuse prevention
|
||||
stop_token_max = 256
|
||||
|
@ -48,7 +49,7 @@ logit_bias_max = 512
|
|||
dry_seq_break_max = 128
|
||||
|
||||
# global vars
|
||||
KcppVersion = "1.84.2"
|
||||
KcppVersion = "1.85"
|
||||
showdebug = True
|
||||
kcpp_instance = None #global running instance
|
||||
global_memory = {"tunnel_url": "", "restart_target":"", "input_to_exit":False, "load_complete":False}
|
||||
|
@ -86,6 +87,7 @@ runmode_untouched = True
|
|||
modelfile_extracted_meta = None
|
||||
importvars_in_progress = False
|
||||
has_multiplayer = False
|
||||
savedata_obj = None
|
||||
multiplayer_story_data_compressed = None #stores the full compressed story of the current multiplayer session
|
||||
multiplayer_turn_major = 1 # to keep track of when a client needs to sync their stories
|
||||
multiplayer_turn_minor = 1
|
||||
|
@ -676,7 +678,7 @@ def string_contains_or_overlaps_sequence_substring(inputstr, sequences):
|
|||
return False
|
||||
|
||||
def get_capabilities():
|
||||
global has_multiplayer, KcppVersion, friendlymodelname, friendlysdmodelname, fullsdmodelpath, mmprojpath, password, fullwhispermodelpath, ttsmodelpath
|
||||
global savedata_obj, has_multiplayer, KcppVersion, friendlymodelname, friendlysdmodelname, fullsdmodelpath, mmprojpath, password, fullwhispermodelpath, ttsmodelpath
|
||||
has_llm = not (friendlymodelname=="inactive")
|
||||
has_txt2img = not (friendlysdmodelname=="inactive" or fullsdmodelpath=="")
|
||||
has_vision = (mmprojpath!="")
|
||||
|
@ -685,7 +687,7 @@ def get_capabilities():
|
|||
has_search = True if args.websearch else False
|
||||
has_tts = (ttsmodelpath!="")
|
||||
admin_type = (2 if args.admin and args.admindir and args.adminpassword else (1 if args.admin and args.admindir else 0))
|
||||
return {"result":"KoboldCpp", "version":KcppVersion, "protected":has_password, "llm":has_llm, "txt2img":has_txt2img,"vision":has_vision,"transcribe":has_whisper,"multiplayer":has_multiplayer,"websearch":has_search,"tts":has_tts, "admin": admin_type}
|
||||
return {"result":"KoboldCpp", "version":KcppVersion, "protected":has_password, "llm":has_llm, "txt2img":has_txt2img,"vision":has_vision,"transcribe":has_whisper,"multiplayer":has_multiplayer,"websearch":has_search,"tts":has_tts, "savedata":(savedata_obj is not None), "admin": admin_type}
|
||||
|
||||
def dump_gguf_metadata(file_path): #if you're gonna copy this into your own project at least credit concedo
|
||||
chunk_size = 1024*1024*12 # read first 12mb of file
|
||||
|
@ -2362,7 +2364,7 @@ Enter Prompt:<br>
|
|||
def do_GET(self):
|
||||
global embedded_kailite, embedded_kcpp_docs, embedded_kcpp_sdui
|
||||
global last_req_time, start_time
|
||||
global has_multiplayer, multiplayer_turn_major, multiplayer_turn_minor, multiplayer_story_data_compressed, multiplayer_dataformat, multiplayer_lastactive, maxctx, maxhordelen, friendlymodelname, lastgeneratedcomfyimg, KcppVersion, totalgens, preloaded_story, exitcounter, currentusergenkey, friendlysdmodelname, fullsdmodelpath, mmprojpath, password
|
||||
global savedata_obj, has_multiplayer, multiplayer_turn_major, multiplayer_turn_minor, multiplayer_story_data_compressed, multiplayer_dataformat, multiplayer_lastactive, maxctx, maxhordelen, friendlymodelname, lastgeneratedcomfyimg, KcppVersion, totalgens, preloaded_story, exitcounter, currentusergenkey, friendlysdmodelname, fullsdmodelpath, mmprojpath, password
|
||||
self.path = self.path.rstrip('/')
|
||||
response_body = None
|
||||
content_type = 'application/json'
|
||||
|
@ -2549,7 +2551,7 @@ Enter Prompt:<br>
|
|||
return
|
||||
|
||||
def do_POST(self):
|
||||
global modelbusy, requestsinqueue, currentusergenkey, totalgens, pendingabortkey, lastgeneratedcomfyimg, multiplayer_turn_major, multiplayer_turn_minor, multiplayer_story_data_compressed, multiplayer_dataformat, multiplayer_lastactive
|
||||
global modelbusy, requestsinqueue, currentusergenkey, totalgens, pendingabortkey, lastgeneratedcomfyimg, multiplayer_turn_major, multiplayer_turn_minor, multiplayer_story_data_compressed, multiplayer_dataformat, multiplayer_lastactive, net_save_slots
|
||||
contlenstr = self.headers['content-length']
|
||||
content_length = 0
|
||||
body = None
|
||||
|
@ -2707,6 +2709,84 @@ Enter Prompt:<br>
|
|||
multiplayer_lastactive[sender] = int(time.time())
|
||||
response_body = (json.dumps({"turn_major":multiplayer_turn_major,"turn_minor":multiplayer_turn_minor,"idle":self.get_multiplayer_idle_state(sender),"data_format":multiplayer_dataformat}).encode())
|
||||
|
||||
elif self.path.endswith('/api/extra/data/list'):
|
||||
if not self.secure_endpoint():
|
||||
return
|
||||
if savedata_obj is None:
|
||||
response_body = (json.dumps([]).encode())
|
||||
return
|
||||
output = []
|
||||
for i in range (net_save_slots):
|
||||
if str(i) in savedata_obj:
|
||||
output.append(savedata_obj[str(i)]["title"])
|
||||
else:
|
||||
output.append("")
|
||||
response_body = (json.dumps(output).encode())
|
||||
|
||||
elif self.path.endswith('/api/extra/data/load'):
|
||||
if not self.secure_endpoint():
|
||||
return
|
||||
if savedata_obj is None:
|
||||
response_body = (json.dumps({"success":False,"data":None}).encode())
|
||||
loadid = -1
|
||||
try:
|
||||
tempbody = json.loads(body)
|
||||
loadid = tryparseint(tempbody.get('slot', 0))
|
||||
except Exception:
|
||||
loadid = -1
|
||||
if loadid < 0 or str(loadid) not in savedata_obj:
|
||||
response_body = (json.dumps({"success":False,"data":None}).encode())
|
||||
else:
|
||||
response_body = (json.dumps({"success":True,"data":savedata_obj[str(loadid)]}).encode())
|
||||
|
||||
elif self.path.endswith('/api/extra/data/save'):
|
||||
if not self.secure_endpoint():
|
||||
return
|
||||
if savedata_obj is None:
|
||||
response_code = 400
|
||||
response_body = (json.dumps({"success":False, "error":"SaveDataFile not enabled!"}).encode())
|
||||
else:
|
||||
try:
|
||||
incoming_story = json.loads(body) # ensure submitted data is valid json
|
||||
slotid = tryparseint(incoming_story.get('slot', -1))
|
||||
dataformat = incoming_story.get('format', "")
|
||||
title = incoming_story.get('title', "")
|
||||
if not title or title=="":
|
||||
title = "Untitled Save"
|
||||
storybody = incoming_story.get('data', None) #should be a compressed string
|
||||
if slotid >= 0 and slotid < net_save_slots: # we shall provide 4 network save slots
|
||||
saveneeded = False
|
||||
if storybody and storybody!="":
|
||||
storybody = str(storybody)
|
||||
if len(storybody) > (1024*1024*8): #limit story to 8mb
|
||||
response_code = 400
|
||||
response_body = (json.dumps({"success":False, "error":"Story is too long!"}).encode())
|
||||
else:
|
||||
savedata_obj[str(slotid)] = {"title":title, "format":dataformat, "data":storybody}
|
||||
saveneeded = True
|
||||
else: #erasing existing story
|
||||
if str(slotid) in savedata_obj:
|
||||
savedata_obj.pop(str(slotid))
|
||||
saveneeded = True
|
||||
if saveneeded:
|
||||
if args.savedatafile and os.path.exists(args.savedatafile):
|
||||
with open(args.savedatafile, 'w+', encoding='utf-8', errors='ignore') as f:
|
||||
json.dump(savedata_obj, f)
|
||||
print(f"Data was saved to slot {slotid}")
|
||||
response_body = (json.dumps({"success":True, "error":""}).encode())
|
||||
else:
|
||||
response_code = 400
|
||||
response_body = (json.dumps({"success":False, "error":"SaveDataFile is missing!"}).encode())
|
||||
else:
|
||||
response_body = (json.dumps({"success":True, "error":""}).encode())
|
||||
else:
|
||||
response_code = 400
|
||||
response_body = (json.dumps({"success":False, "error":"No story submitted or invalid slot!"}).encode())
|
||||
except Exception as e:
|
||||
utfprint("Remote Save Story - Body Error: " + str(e))
|
||||
response_code = 400
|
||||
response_body = (json.dumps({"success": False, "error":"Submitted story invalid!"}).encode())
|
||||
|
||||
elif self.path.endswith('/api/extra/multiplayer/getstory'):
|
||||
if not self.secure_endpoint():
|
||||
return
|
||||
|
@ -3097,7 +3177,7 @@ def show_gui():
|
|||
global using_gui_launcher
|
||||
using_gui_launcher = True
|
||||
from tkinter.filedialog import askopenfilename, askdirectory
|
||||
from tkinter.filedialog import asksaveasfile
|
||||
from tkinter.filedialog import asksaveasfilename
|
||||
|
||||
# if args received, launch
|
||||
if len(sys.argv) != 1 and not args.showgui:
|
||||
|
@ -3214,7 +3294,7 @@ def show_gui():
|
|||
|
||||
tabs = ctk.CTkFrame(root, corner_radius = 0, width=windowwidth, height=windowheight-50)
|
||||
tabs.grid(row=0, stick="nsew")
|
||||
tabnames= ["Quick Launch", "Hardware", "Tokens", "Model Files", "Network", "Horde Worker","Image Gen","Audio","Admin","Extra"]
|
||||
tabnames= ["Quick Launch", "Hardware", "Tokens", "Loaded Files", "Network", "Horde Worker","Image Gen","Audio","Admin","Extra"]
|
||||
navbuttons = {}
|
||||
navbuttonframe = ctk.CTkFrame(tabs, width=100, height=int(tabs.cget("height")))
|
||||
navbuttonframe.grid(row=0, column=0, padx=2,pady=2)
|
||||
|
@ -3276,6 +3356,7 @@ def show_gui():
|
|||
lora_var = ctk.StringVar()
|
||||
lora_base_var = ctk.StringVar()
|
||||
preloadstory_var = ctk.StringVar()
|
||||
savedatafile_var = ctk.StringVar()
|
||||
mmproj_var = ctk.StringVar()
|
||||
visionmaxres_var = ctk.StringVar(value=str(default_visionmaxres))
|
||||
draftmodel_var = ctk.StringVar()
|
||||
|
@ -3385,14 +3466,22 @@ def show_gui():
|
|||
entry.grid(row=row, column=(0 if singleline else 1), padx=padx, sticky="nw")
|
||||
return entry, label
|
||||
|
||||
def makefileentry(parent, text, searchtext, var, row=0, width=200, filetypes=[], onchoosefile=None, singlerow=False, singlecol=True, is_dir=False, tooltiptxt=""):
|
||||
#file dialog types: 0=openfile,1=savefile,2=opendir
|
||||
def makefileentry(parent, text, searchtext, var, row=0, width=200, filetypes=[], onchoosefile=None, singlerow=False, singlecol=True, dialog_type=0, tooltiptxt=""):
|
||||
label = makelabel(parent, text, row,0,tooltiptxt,columnspan=3)
|
||||
def getfilename(var, text):
|
||||
initialDir = os.path.dirname(var.get())
|
||||
initialDir = initialDir if os.path.isdir(initialDir) else None
|
||||
fnam = None
|
||||
if is_dir:
|
||||
if dialog_type==2:
|
||||
fnam = askdirectory(title=text, mustexist=True, initialdir=initialDir)
|
||||
elif dialog_type==1:
|
||||
fnam = asksaveasfilename(title=text, filetypes=filetypes, defaultextension=filetypes, initialdir=initialDir)
|
||||
if not fnam:
|
||||
fnam = ""
|
||||
else:
|
||||
fnam = str(fnam).strip()
|
||||
fnam = f"{fnam}.jsondb" if ".jsondb" not in fnam.lower() else fnam
|
||||
else:
|
||||
fnam = askopenfilename(title=text,filetypes=filetypes, initialdir=initialDir)
|
||||
if fnam:
|
||||
|
@ -3777,7 +3866,7 @@ def show_gui():
|
|||
togglectxshift(1,1,1)
|
||||
|
||||
# Model Tab
|
||||
model_tab = tabcontent["Model Files"]
|
||||
model_tab = tabcontent["Loaded Files"]
|
||||
|
||||
makefileentry(model_tab, "Text Model:", "Select GGUF or GGML Model File", model_var, 1,width=280,singlerow=True, onchoosefile=on_picked_model_file,tooltiptxt="Select a GGUF or GGML model file on disk to be loaded.")
|
||||
makefileentry(model_tab, "Text Lora:", "Select Lora File",lora_var, 3,width=280,singlerow=True,tooltiptxt="Select an optional GGML Text LoRA adapter to use.\nLeave blank to skip.")
|
||||
|
@ -3789,6 +3878,7 @@ def show_gui():
|
|||
makelabelentry(model_tab, "Splits: ", draftgpusplit_str_vars, 13, 50,padx=210,singleline=True,tooltip="Distribution of draft model layers. Leave blank to follow main model's gpu split. Only works if multi-gpu (All) selected in main model.", labelpadx=160)
|
||||
makelabelentry(model_tab, "Layers: ", draftgpulayers_var, 13, 50,padx=320,singleline=True,tooltip="How many layers to GPU offload for the draft model", labelpadx=270)
|
||||
makefileentry(model_tab, "Preload Story:", "Select Preloaded Story File", preloadstory_var, 15,width=280,singlerow=True,tooltiptxt="Select an optional KoboldAI JSON savefile \nto be served on launch to any client.")
|
||||
makefileentry(model_tab, "SaveData File:", "Select or Create New SaveData Database File", savedatafile_var, 17,width=280,filetypes=[("KoboldCpp SaveDB", "*.jsondb")],singlerow=True,dialog_type=1,tooltiptxt="Selecting a file will allow data to be loaded and saved persistently to this KoboldCpp server remotely. File is created if it does not exist.")
|
||||
makefileentry(model_tab, "ChatCompletions Adapter:", "Select ChatCompletions Adapter File", chatcompletionsadapter_var, 24, width=250, filetypes=[("JSON Adapter", "*.json")], tooltiptxt="Select an optional ChatCompletions Adapter JSON file to force custom instruct tags.")
|
||||
def pickpremadetemplate():
|
||||
initialDir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'kcpp_adapters')
|
||||
|
@ -3909,7 +3999,7 @@ def show_gui():
|
|||
admin_tab = tabcontent["Admin"]
|
||||
makecheckbox(admin_tab, "Enable Model Administration", admin_var, 1, 0,tooltiptxt="Enable a admin server, allowing you to remotely relaunch and swap models and configs.")
|
||||
makelabelentry(admin_tab, "Admin Password:" , admin_password_var, 3, 150,padx=120,singleline=True,tooltip="Require a password to access admin functions. You are strongly advised to use one for publically accessible instances!")
|
||||
makefileentry(admin_tab, "Config Directory:", "Select directory containing .kcpps files to relaunch from", admin_dir_var, 5, width=280, is_dir=True, tooltiptxt="Specify a directory to look for .kcpps configs in, which can be used to swap models.")
|
||||
makefileentry(admin_tab, "Config Directory:", "Select directory containing .kcpps files to relaunch from", admin_dir_var, 5, width=280, dialog_type=2, tooltiptxt="Specify a directory to look for .kcpps configs in, which can be used to swap models.")
|
||||
|
||||
def kcpp_export_template():
|
||||
nonlocal kcpp_exporting_template
|
||||
|
@ -3937,10 +4027,10 @@ def show_gui():
|
|||
savdict["draftgpusplit"] = None
|
||||
savdict["config"] = None
|
||||
savdict["ttsthreads"] = 0
|
||||
filename = asksaveasfile(filetypes=file_type, defaultextension=file_type)
|
||||
if filename is None:
|
||||
filename = asksaveasfilename(filetypes=file_type, defaultextension=file_type)
|
||||
if not filename:
|
||||
return
|
||||
filenamestr = str(filename.name).strip()
|
||||
filenamestr = str(filename).strip()
|
||||
filenamestr = f"{filenamestr}.kcppt" if ".kcppt" not in filenamestr.lower() else filenamestr
|
||||
file = open(filenamestr, 'a')
|
||||
file.write(json.dumps(savdict))
|
||||
|
@ -4067,6 +4157,7 @@ def show_gui():
|
|||
args.model_param = None if model_var.get() == "" else model_var.get()
|
||||
args.lora = None if lora_var.get() == "" else ([lora_var.get()] if lora_base_var.get()=="" else [lora_var.get(), lora_base_var.get()])
|
||||
args.preloadstory = None if preloadstory_var.get() == "" else preloadstory_var.get()
|
||||
args.savedatafile = None if savedatafile_var.get() == "" else savedatafile_var.get()
|
||||
try:
|
||||
if kcpp_exporting_template and isinstance(args.preloadstory, str) and args.preloadstory!="" and os.path.exists(args.preloadstory):
|
||||
print("Embedding preload story...") # parse and save embedded preload story
|
||||
|
@ -4274,6 +4365,7 @@ def show_gui():
|
|||
|
||||
password_var.set(dict["password"] if ("password" in dict and dict["password"]) else "")
|
||||
preloadstory_var.set(dict["preloadstory"] if ("preloadstory" in dict and dict["preloadstory"]) else "")
|
||||
savedatafile_var.set(dict["savedatafile"] if ("savedatafile" in dict and dict["savedatafile"]) else "")
|
||||
chatcompletionsadapter_var.set(dict["chatcompletionsadapter"] if ("chatcompletionsadapter" in dict and dict["chatcompletionsadapter"]) else "")
|
||||
port_var.set(dict["port_param"] if ("port_param" in dict and dict["port_param"]) else defaultport)
|
||||
host_var.set(dict["host"] if ("host" in dict and dict["host"]) else "")
|
||||
|
@ -4324,10 +4416,10 @@ def show_gui():
|
|||
export_vars()
|
||||
savdict = json.loads(json.dumps(args.__dict__))
|
||||
file_type = [("KoboldCpp Settings", "*.kcpps")]
|
||||
filename = asksaveasfile(filetypes=file_type, defaultextension=file_type)
|
||||
if filename is None:
|
||||
filename = asksaveasfilename(filetypes=file_type, defaultextension=file_type)
|
||||
if not filename:
|
||||
return
|
||||
filenamestr = str(filename.name).strip()
|
||||
filenamestr = str(filename).strip()
|
||||
filenamestr = f"{filenamestr}.kcpps" if ".kcpps" not in filenamestr.lower() else filenamestr
|
||||
file = open(filenamestr, 'a')
|
||||
file.write(json.dumps(savdict))
|
||||
|
@ -5282,7 +5374,7 @@ def kcpp_main_process(launch_args, g_memory=None, gui_launcher=False):
|
|||
friendlymodelname = "koboldcpp/" + sanitize_string(newmdldisplayname)
|
||||
|
||||
# horde worker settings
|
||||
global maxhordelen, maxhordectx, showdebug, has_multiplayer
|
||||
global maxhordelen, maxhordectx, showdebug, has_multiplayer, savedata_obj
|
||||
if args.hordemodelname and args.hordemodelname!="":
|
||||
friendlymodelname = args.hordemodelname
|
||||
if args.debugmode == 1:
|
||||
|
@ -5303,6 +5395,25 @@ def kcpp_main_process(launch_args, g_memory=None, gui_launcher=False):
|
|||
if args.multiplayer:
|
||||
has_multiplayer = True
|
||||
|
||||
if args.savedatafile and isinstance(args.savedatafile, str):
|
||||
filepath = args.savedatafile
|
||||
try:
|
||||
with open(filepath, 'r+', encoding='utf-8', errors='ignore') as f:
|
||||
loaded = json.load(f)
|
||||
savedata_obj = loaded
|
||||
print(f"Loaded existing savedatafile at '{filepath}'.")
|
||||
except FileNotFoundError:
|
||||
try:
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
with open(filepath, 'w+', encoding='utf-8', errors='ignore') as f:
|
||||
savedata_obj = {}
|
||||
print(f"File '{filepath}' did not exist. Created new savedatafile.")
|
||||
json.dump(savedata_obj, f)
|
||||
except Exception as e:
|
||||
print(f"Failed to create savedatafile '{filepath}': {e}")
|
||||
except Exception as e:
|
||||
print(f"Failed to access savedatafile '{filepath}': {e}")
|
||||
|
||||
if args.highpriority:
|
||||
print("Setting process to Higher Priority - Use Caution")
|
||||
try:
|
||||
|
@ -5812,6 +5923,7 @@ if __name__ == '__main__':
|
|||
advparser.add_argument("--highpriority", help="Experimental flag. If set, increases the process CPU priority, potentially speeding up generation. Use caution.", action='store_true')
|
||||
advparser.add_argument("--foreground", help="Windows only. Sends the terminal to the foreground every time a new prompt is generated. This helps avoid some idle slowdown issues.", action='store_true')
|
||||
advparser.add_argument("--preloadstory", metavar=('[savefile]'), help="Configures a prepared story json save file to be hosted on the server, which frontends (such as KoboldAI Lite) can access over the API.", default="")
|
||||
advparser.add_argument("--savedatafile", metavar=('[savefile]'), help="If enabled, creates or opens a persistent database file on the server, that allows users to save and load their data remotely.", default="")
|
||||
advparser.add_argument("--quiet", help="Enable quiet mode, which hides generation inputs and outputs in the terminal. Quiet mode is automatically enabled when running a horde worker.", action='store_true')
|
||||
advparser.add_argument("--ssl", help="Allows all content to be served over SSL instead. A valid UNENCRYPTED SSL cert and key .pem files must be provided", metavar=('[cert_pem]', '[key_pem]'), nargs='+')
|
||||
advparser.add_argument("--nocertify", help="Allows insecure SSL connections. Use this if you have cert errors and need to bypass certificate restrictions.", action='store_true')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue