Merge branch 'overhaul' into united

This commit is contained in:
Henk 2022-06-15 02:09:30 +02:00
commit f49cf919bf
9 changed files with 2179 additions and 1198 deletions

File diff suppressed because it is too large Load Diff

2
pytest.ini Normal file
View File

@ -0,0 +1,2 @@
[pytest]
addopts = --ignore=miniconda3 --ignore=runtime --html=unit_test_report.html --self-contained-html -v

View File

@ -221,7 +221,7 @@ This project contains work from the following contributors :
* The Gantian - Creator of KoboldAI, has created most features such as the interface, the different AI model / API integrations and in general the largest part of the project.
* VE FORBRYDERNE - Contributed many features such as the Editing overhaul, Adventure Mode, expansions to the world info section, breakmodel integration, scripting support, softpromtps and much more. As well as vastly improving the TPU compatibility and integrating external code into KoboldAI so we could use official versions of Transformers with virtually no downsides.
* Henk717 - Contributed the installation scripts, this readme, random story generator, the docker scripts, the foundation for the commandline interface and other smaller changes as well as integrating multiple parts of the code of different forks to unite it all. He also optimized the model loading so that downloaded models get converted to efficient offline models and that in future models are more likely to work out of the box. Not all code Github attributes to Henk717 is by Henk717 as some of it has been integrations of other people's work. We try to clarify this in the contributors list as much as we can.
* Ebolam - Automatic Saving
* Ebolam - Automatic Saving, back/redo, pinning, web loading of models
* Frogging101 - top\_k / tfs support (Part of this support was later redone by VE to integrate what was originally inside of finetuneanon's transformers)
* UWUplus (Ralf) - Contributed storage systems for community colabs, as well as cleaning up and integrating the website dependencies/code better. He is also the maintainer of flask-cloudflared which we use to generate the cloudflare links.
* Javalar - Initial Performance increases on the story\_refresh
@ -238,4 +238,4 @@ Did we miss your contribution? Feel free to issue a commit adding your name to t
KoboldAI is licensed with a AGPL license, in short this means that it can be used by anyone for any purpose. However, if you decide to make a publicly available instance your users are entitled to a copy of the source code including all modifications that you have made (which needs to be available trough an interface such as a button on your website), you may also not distribute this project in a form that does not contain the source code (Such as compiling / encrypting the code and distributing this version without also distributing the source code that includes the changes that you made. You are allowed to distribute this in a closed form if you also provide a separate archive with the source code.).
umamba.exe is bundled for convenience because we observed that many of our users had trouble with command line download methods, it is not part of our project and does not fall under the AGPL license. It is licensed under the BSD-3-Clause license. Other files with differing licenses will have a reference or embedded version of this license within the file.
umamba.exe is bundled for convenience because we observed that many of our users had trouble with command line download methods, it is not part of our project and does not fall under the AGPL license. It is licensed under the BSD-3-Clause license. Other files with differing licenses will have a reference or embedded version of this license within the file.

View File

@ -7,6 +7,7 @@ var socket;
// UI references for jQuery
var connect_status;
var button_loadmodel;
var button_newgame;
var button_rndgame;
var button_save;
@ -56,6 +57,7 @@ var savepins;
var topic;
var saveas_accept;
var saveas_close;
var loadmodelpopup;
var loadpopup;
var loadcontent;
var load_accept;
@ -99,6 +101,7 @@ var remote = false;
var gamestate = "";
var gamesaved = true;
var modelname = null;
var model = "";
// This is true iff [we're in macOS and the browser is Safari] or [we're in iOS]
var using_webkit_patch = true;
@ -152,6 +155,12 @@ function getThrottle(ms) {
}
}
function reset_menus() {
settings_menu.html("");
format_menu.html("");
wi_menu.html("");
}
function addSetting(ob) {
// Add setting block to Settings Menu
if(ob.uitype == "slider"){
@ -877,6 +886,7 @@ function formatChunkInnerText(chunk) {
}
function dosubmit(disallow_abort) {
submit_start = Date.now();
var txt = input_text.val().replace(/\u00a0/g, " ");
if((disallow_abort || gamestate !== "wait") && !memorymode && !gamestarted && ((!adventure || !action_mode) && txt.trim().length == 0)) {
return;
@ -947,6 +957,17 @@ function sendSaveAsRequest() {
socket.send({'cmd': 'saveasrequest', 'data': {"name": saveasinput.val(), "pins": savepins.val()}});
}
function showLoadModelPopup() {
loadmodelpopup.removeClass("hidden");
loadmodelpopup.addClass("flex");
}
function hideLoadModelPopup() {
loadmodelpopup.removeClass("flex");
loadmodelpopup.addClass("hidden");
loadmodelcontent.html("");
}
function showLoadPopup() {
loadpopup.removeClass("hidden");
loadpopup.addClass("flex");
@ -990,6 +1011,92 @@ function hideSamplersPopup() {
samplerspopup.addClass("hidden");
}
function buildLoadModelList(ar, menu, breadcrumbs, showdelete) {
disableButtons([load_model_accept]);
loadmodelcontent.html("");
$("#loadmodellistbreadcrumbs").html("");
$("#custommodelname").addClass("hidden");
var i;
for(i=0; i<breadcrumbs.length; i++) {
$("#loadmodellistbreadcrumbs").append("<button class=\"breadcrumbitem\" id='model_breadcrumbs"+i+"' name='"+ar[0][1]+"' value='"+breadcrumbs[i][0]+"'>"+breadcrumbs[i][1]+"</button><font color=white>\\</font>");
$("#model_breadcrumbs"+i).off("click").on("click", (function () {
return function () {
socket.send({'cmd': 'selectmodel', 'data': $(this).attr("name"), 'folder': $(this).attr("value")});
disableButtons([load_model_accept]);
}
})(i));
}
if (breadcrumbs.length > 0) {
$("#loadmodellistbreadcrumbs").append("<hr size='1'>")
}
for(i=0; i<ar.length; i++) {
if (Array.isArray(ar[i][0])) {
full_path = ar[i][0][0];
folder = ar[i][0][1];
} else {
full_path = "";
folder = ar[i][0];
}
var html
html = "<div class=\"flex\">\
<div class=\"loadlistpadding\"></div>"
//if the menu item is a link to another menu
if(ar[i][3]) {
html = html + "<span class=\"loadlisticon loadmodellisticon-folder oi oi-folder allowed\" aria-hidden=\"true\"></span>"
} else {
//this is a model
html = html + "<div class=\"loadlistpadding\"></div>"
}
//now let's do the delete icon if applicable
if (['NeoCustom', 'GPT2Custom'].includes(menu) && !ar[i][3] && showdelete) {
html = html + "<span class=\"loadlisticon loadmodellisticon-folder oi oi-x allowed\" aria-hidden=\"true\" onclick='if(confirm(\"This will delete the selected folder with all contents. Are you sure?\")) { socket.send({\"cmd\": \"delete_model\", \"data\": \""+full_path.replaceAll("\\", "\\\\")+"\", \"menu\": \""+menu+"\"});}'></span>"
} else {
html = html + "<div class=\"loadlistpadding\"></div>"
}
html = html + "<div class=\"loadlistpadding\"></div>\
<div class=\"loadlistitem\" id=\"loadmodel"+i+"\" name=\""+ar[i][1]+"\" pretty_name=\""+full_path+"\">\
<div>"+folder+"</div>\
<div class=\"flex-push-right\">"+ar[i][2]+"</div>\
</div>\
</div>"
loadmodelcontent.append(html);
//If this is a menu
if(ar[i][3]) {
$("#loadmodel"+i).off("click").on("click", (function () {
return function () {
socket.send({'cmd': 'list_model', 'data': $(this).attr("name"), 'pretty_name': $(this).attr("pretty_name")});
disableButtons([load_model_accept]);
}
})(i));
//If we're in the custom load menu (we need to send the path data back in that case)
} else if(['NeoCustom', 'GPT2Custom'].includes(menu)) {
$("#loadmodel"+i).off("click").on("click", (function () {
return function () {
socket.send({'cmd': 'selectmodel', 'data': $(this).attr("name"), 'path': $(this).attr("pretty_name")});
highlightLoadLine($(this));
}
})(i));
$("#custommodelname").removeClass("hidden");
$("#custommodelname")[0].setAttribute("menu", menu);
//Normal load
} else {
$("#loadmodel"+i).off("click").on("click", (function () {
return function () {
$("#use_gpu_div").addClass("hidden");
$("#modelkey").addClass("hidden");
$("#modellayers").addClass("hidden");
socket.send({'cmd': 'selectmodel', 'data': $(this).attr("name")});
highlightLoadLine($(this));
}
})(i));
}
}
}
function buildLoadList(ar) {
disableButtons([load_accept]);
loadcontent.html("");
@ -1148,6 +1255,7 @@ function buildSamplerList(samplers) {
function highlightLoadLine(ref) {
$("#loadlistcontent > div > div.popuplistselected").removeClass("popuplistselected");
$("#loadmodellistcontent > div > div.popuplistselected").removeClass("popuplistselected");
ref.addClass("popuplistselected");
}
@ -1851,6 +1959,30 @@ function unbindGametext() {
gametext_bound = false;
}
function update_gpu_layers() {
var gpu_layers
gpu_layers = 0;
for (let i=0; i < $("#gpu_count")[0].value; i++) {
gpu_layers += parseInt($("#gpu_layers"+i)[0].value);
$("#gpu_layers_box_"+i)[0].value=$("#gpu_layers"+i)[0].value;
}
if (gpu_layers > parseInt(document.getElementById("gpu_layers_max").innerHTML)) {
disableButtons([load_model_accept]);
$("#gpu_layers_current").html("<span style='color: red'>"+gpu_layers+"/"+ document.getElementById("gpu_layers_max").innerHTML +"</span>");
} else {
enableButtons([load_model_accept]);
$("#gpu_layers_current").html(gpu_layers+"/"+document.getElementById("gpu_layers_max").innerHTML);
}
}
function RemoveAllButFirstOption(selectElement) {
var i, L = selectElement.options.length - 1;
for(i = L; i >= 1; i--) {
selectElement.remove(i);
}
}
//=================================================================//
// READY/RUNTIME
//=================================================================//
@ -1859,6 +1991,8 @@ $(document).ready(function(){
// Bind UI references
connect_status = $('#connectstatus');
button_loadmodel = $('#btn_loadmodel');
button_showmodel = $('#btn_showmodel');
button_newgame = $('#btn_newgame');
button_rndgame = $('#btn_rndgame');
button_save = $('#btn_save');
@ -1912,9 +2046,13 @@ $(document).ready(function(){
saveas_accept = $("#btn_saveasaccept");
saveas_close = $("#btn_saveasclose");
loadpopup = $("#loadcontainer");
loadmodelpopup = $("#loadmodelcontainer");
loadcontent = $("#loadlistcontent");
loadmodelcontent = $("#loadmodellistcontent");
load_accept = $("#btn_loadaccept");
load_close = $("#btn_loadclose");
load_model_accept = $("#btn_loadmodelaccept");
load_model_close = $("#btn_loadmodelclose");
sppopup = $("#spcontainer");
spcontent = $("#splistcontent");
sp_accept = $("#btn_spaccept");
@ -1941,6 +2079,7 @@ $(document).ready(function(){
socket = io.connect(window.document.origin, {transports: ['polling', 'websocket'], closeOnBeforeunload: false});
socket.on('from_server', function(msg) {
//console.log(msg);
if(msg.cmd == "connected") {
// Connected to Server Actions
sman_allow_delete = msg.hasOwnProperty("smandelete") && msg.smandelete;
@ -1954,9 +2093,7 @@ $(document).ready(function(){
connect_status.removeClass("color_orange");
connect_status.addClass("color_green");
// Reset Menus
settings_menu.html("");
format_menu.html("");
wi_menu.html("");
reset_menus();
// Set up "Allow Editing"
$('body').on('input', autofocus);
$('#allowediting').prop('checked', allowedit).prop('disabled', false).change().off('change').on('change', function () {
@ -2018,6 +2155,10 @@ $(document).ready(function(){
scrollToBottom();
} else if(msg.cmd == "updatechunk") {
hideMessage();
if (typeof submit_start !== 'undefined') {
$("#runtime")[0].innerHTML = `Generation time: ${Math.round((Date.now() - submit_start)/1000)} sec`;
delete submit_start;
}
var index = msg.data.index;
var html = msg.data.html;
var existingChunk = game_text.children('#n' + index);
@ -2063,14 +2204,17 @@ $(document).ready(function(){
enableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
hideWaitAnimation();
gamestate = "ready";
favicon.stop_swap();
} else if(msg.data == "wait") {
gamestate = "wait";
disableSendBtn();
disableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
showWaitAnimation();
favicon.start_swap();
} else if(msg.data == "start") {
setStartState();
gamestate = "ready";
favicon.stop_swap();
}
} else if(msg.cmd == "allowsp") {
allowsp = !!msg.data;
@ -2219,6 +2363,8 @@ $(document).ready(function(){
} else if(msg.cmd == "setanotetemplate") {
// Set contents of Author's Note Template field
$("#anotetemplate").val(msg.data);
} else if(msg.cmd == "reset_menus") {
reset_menus();
} else if(msg.cmd == "addsetting") {
// Add setting controls
addSetting(msg.data);
@ -2425,6 +2571,88 @@ $(document).ready(function(){
} else {
debug_area.addClass("hidden");
}
} else if(msg.cmd == 'show_model_menu') {
//console.log(msg)
$("#use_gpu_div").addClass("hidden");
$("#modelkey").addClass("hidden");
$("#modellayers").addClass("hidden");
$("#oaimodel").addClass("hidden")
buildLoadModelList(msg.data, msg.menu, msg.breadcrumbs, msg.showdelete);
} else if(msg.cmd == 'selected_model_info') {
enableButtons([load_model_accept]);
$("#oaimodel").addClass("hidden")
$("#oaimodel")[0].options[0].selected = true;
if (msg.key) {
$("#modelkey").removeClass("hidden");
$("#modelkey")[0].value = msg.key_value;
} else {
$("#modelkey").addClass("hidden");
}
if (msg.url) {
$("#modelurl").removeClass("hidden");
} else {
$("#modelurl").addClass("hidden");
}
if (msg.gpu) {
$("#use_gpu_div").removeClass("hidden");
} else {
$("#use_gpu_div").addClass("hidden");
}
if (msg.breakmodel) {
var html;
$("#modellayers").removeClass("hidden");
html = "";
for (let i = 0; i < msg.gpu_names.length; i++) {
html += "GPU " + i + " " + msg.gpu_names[i] + ": ";
html += '<input inputmode="numeric" id="gpu_layers_box_'+i+'" class="justifyright flex-push-right model_layers" value="'+msg.break_values[i]+'" ';
html += 'onblur=\'$("#gpu_layers'+i+'")[0].value=$("#gpu_layers_box_'+i+'")[0].value;update_gpu_layers();\'>';
html += "<input type='range' class='form-range airange' min='0' max='"+msg.layer_count+"' step='1' value='"+msg.break_values[i]+"' id='gpu_layers"+i+"' onchange='update_gpu_layers();'>";
}
$("#model_layer_bars").html(html);
$("#gpu_layers_max").html(msg.layer_count);
$("#gpu_count")[0].value = msg.gpu_count;
update_gpu_layers();
} else {
$("#modellayers").addClass("hidden");
}
} else if(msg.cmd == 'oai_engines') {
$("#oaimodel").removeClass("hidden")
selected_item = 0;
length = $("#oaimodel")[0].options.length;
for (let i = 0; i < length; i++) {
$("#oaimodel")[0].options.remove(1);
}
msg.data.forEach(function (item, index) {
var option = document.createElement("option");
option.value = item[0];
option.text = item[1];
if(msg.online_model == item[0]) {
selected_item = index+1;
}
$("#oaimodel")[0].appendChild(option);
if(selected_item != "") {
$("#oaimodel")[0].options[selected_item].selected = true;
}
})
} else if(msg.cmd == 'show_model_name') {
$("#showmodelnamecontent").html("<div class=\"flex\"><div class=\"loadlistpadding\"></div><div class=\"loadlistitem\">" + msg.data + "</div></div>");
$("#showmodelnamecontainer").removeClass("hidden");
} else if(msg.cmd == 'hide_model_name') {
$("#showmodelnamecontainer").addClass("hidden");
//console.log("Closing window");
} else if(msg.cmd == 'model_load_status') {
$("#showmodelnamecontent").html("<div class=\"flex\"><div class=\"loadlistpadding\"></div><div class=\"loadlistitem\" style='align: left'>" + msg.data + "</div></div>");
$("#showmodelnamecontainer").removeClass("hidden");
//console.log(msg.data);
} else if(msg.cmd == 'oai_engines') {
RemoveAllButFirstOption($("#oaimodel")[0]);
for (const engine of msg.data) {
var opt = document.createElement('option');
opt.value = engine[0];
opt.innerHTML = engine[1];
$("#oaimodel")[0].appendChild(opt);
}
}
});
@ -2673,12 +2901,35 @@ $(document).ready(function(){
hideLoadPopup();
});
load_model_close.on("click", function(ev) {
$("#modellayers").addClass("hidden");
hideLoadModelPopup();
});
load_accept.on("click", function(ev) {
hideMessage();
newly_loaded = true;
socket.send({'cmd': 'loadrequest', 'data': ''});
hideLoadPopup();
});
load_model_accept.on("click", function(ev) {
hideMessage();
var gpu_layers;
var message;
if($("#modellayers")[0].classList.contains('hidden')) {
gpu_layers = ","
} else {
gpu_layers = ""
for (let i=0; i < $("#gpu_count")[0].value; i++) {
gpu_layers += $("#gpu_layers"+i)[0].value + ",";
}
}
message = {'cmd': 'load_model', 'use_gpu': $('#use_gpu')[0].checked, 'key': $('#modelkey')[0].value, 'gpu_layers': gpu_layers.slice(0, -1), 'url': $('#modelurl')[0].value, 'online_model': $('#oaimodel')[0].value};
socket.send(message);
loadmodelcontent.html("");
hideLoadModelPopup();
});
sp_close.on("click", function(ev) {
hideSPPopup();
@ -2712,6 +2963,14 @@ $(document).ready(function(){
hideSamplersPopup();
});
button_loadmodel.on("click", function(ev) {
showLoadModelPopup();
socket.send({'cmd': 'list_model', 'data': 'mainmenu'});
});
button_showmodel.on("click", function(ev) {
socket.send({'cmd': 'show_model', 'data': ''});
});
button_newgame.on("click", function(ev) {
if(connected) {
showNewStoryPopup();

View File

@ -368,14 +368,14 @@ body.connected #popupfooter, #popupfooter.always-available {
margin-top: 200px;
}
#loadpopup {
.loadpopup {
width: 500px;
background-color: #262626;
margin-top: 100px;
}
@media (max-width: 768px) {
#loadpopup {
.loadpopup {
width: 100%;
background-color: #262626;
margin-top: 100px;
@ -1055,7 +1055,7 @@ body.connected .statusiconlabel, .statusiconlabel.always-available {
}
.loadlistitem {
padding: 5px 10px 5px 10px;
padding: 0px 0px 0px 0px;
display: flex;
flex-grow: 1;
color: #ffffff;
@ -1071,6 +1071,28 @@ body.connected .statusiconlabel, .statusiconlabel.always-available {
background-color: #688f1f;
}
.breadcrumbitem {
padding: 5px 10px 5px 10px;
color: #ffffff;
background-color: transparent;
border: none;
-moz-transition: background-color 0.25s ease-in;
-o-transition: background-color 0.25s ease-in;
-webkit-transition: background-color 0.25s ease-in;
transition: background-color 0.25s ease-in;
}
.breadcrumbitem:hover {
cursor: pointer;
background-color: #688f1f;
}
hr {
padding: 0px;
margin: 0px;
}
.loadlistpadding {
padding-right: 10px;
}
@ -1462,3 +1484,65 @@ body.connected .popupfooter, .popupfooter.always-available {
overflow: hidden;
font-size: 12pt;
}
.model_layers {
width: 3ch;
background-color: inherit;
border: none;
outline: none;
}
.model_layers:focus {
color: #cdf;
}
.menu_icon {
position: fixed;
top:10px;
left: 5px;
z-index:100;
display: inline-block;
cursor: pointer;
}
.SideMenu {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
.SideMenu.open {
width: 450px;
}
@media only screen and (max-width: 768px) {
.SideMenu.open {
width: 100%;
}
}
.menubar1, .menubar2, .menubar3 {
width: 21px;
height: 3px;
background-color: #999;
margin: 3px 0;
transition: 0.4s;
}
.change .menubar1 {
transform: translate(0px, 6px) rotate(-45deg);
}
.change .menubar2 {opacity: 0;}
.change .menubar3 {
transform: translate(0px, -6px) rotate(45deg);
}

64
static/favicon.js Normal file
View File

@ -0,0 +1,64 @@
// Global Definitions
var fav_icon2 = "data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB+1BMVEUAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAAASFhBBWD4iUyoFEwgFEwguUTM+VDoMFAwAAAA+elIudz8AAAAAAAA0MigyLyQAAAAbLh1LdElSbUoVMBkAAABAZ0M2fkUAAAABAQFMiGQraDkAAQANFxEGFQkLFg8EEAYAAAAsZDonZjUAAABCgVVAnFYrSjhEjFpFi1sdRScAAAAjOi8VMxx1dGOFgGYAAABOTEabmIdlYlQaGhgaGhddXFauqY5JRjoAAAAAAAABAQFGeExIl1lX0XRW0XRHi1RFe02vv5W31KFd1Hpc1Hpe1HvO1KvDvJlqZ1plYVOmoIVt1IFl1H7AuZp1cV9jX1AmSCw3Nzg7NmA1MTJuz4Bm1H5MST9HPl9BQEMgNiNXgWKiobFgXICDd5dfw3RZVnJiV3zGv9Bqf29Oj2G/v8hTTpGhl8dbxHVd0npiYoxhWJvIxtlcimZFn1lRclg9SkZNblZBeEpDbEZCa0ZBc0hLY1BAS1BdaV87j01Vx3FWynJSrGZOhlVasGtas2xatm1at21WnWJQm15WyXJQvmlavnBZrGlEYEJWe1RBWz9Um2BavXBgxn9XhllGY0RLaklXiFlTwG5OpmVSfFNMbUpGZEVLa0lShldEhVCChHiKiHvWz6/Kw6WWlZGAfmj///8kr0X+AAAARHRSTlMAASFrcAhxIjLb/vWvsPb+20b4+DFFyMkz2vf43CP9/m5y9vZysLGvsQn19mz+/tz4+NxHycr3+Ejb/vaxsPX+3TRtcBrzrrgAAAABYktHRKhQCDaSAAAAB3RJTUUH5gYJFyQy3tftxgAAAQBJREFUGNNjYGBgYGRiZmFlZWNmZ2SAAA5OLm4eXj5+AQ6ogKCQi6ubu4ensCCIxygiKubl7ePr6+cfIC4owcjAJCkVGBQc4usbGhYeIS0jy8AsFxkVHRPr6xsXn5CYJK/AoKiUnJKalg5UkZGZla2swsCqmpObl1/g61tYVFxSqsbKwKpeVl5RWVVdU1tX39CoocnAotXU3NLa1t7R2dXd06utwqCj6+vb1z9h4sRJk6f4+uopMLDrG0z1nTZ94sQZM31nGRrJMjBKGJvMnjN3wrz5CxaaCnKAvSNqtmjxkqXLlptbQP0iYmllbWNrZ+/gCBVgZHdS1GR1VpAFqQcApI0/jqlZOvEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjItMDYtMDlUMjM6MzY6NTArMDA6MDDi0xr+AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIyLTA2LTA5VDIzOjM2OjUwKzAwOjAwk46iQgAAAABJRU5ErkJggg==";
var fav_icon1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB+FBMVEUAAAAAAAAAAAAAAAAAAAEAAAAAAQEAAAAAAAAUFRlLVGYrSWgHEBoHEBk3S19HUGMOExkAAABOcos7apIAAAAAAAA2Ly01KyoAAAAgKzdVaX9bZHIaKzwAAABKYHhDcZgAAAABAQFfgJY2XX0AAQEQFhoIEhwOFRgGDRUAAAAAAQE3W3cyWnwAAABSeJJRjLs1R1FVgaFWgJ4lPlMAAAAsOD4aLj55bm2Md3QAAABPSkmfko9pXlsbGRkbGRlfWlm1oJxMQkAAAAAAAAABAQFTb4tYibFtvPpWgKNScpC6s7nExtNzwPp1wPnZx8jMsKtuZGFoXVutmJODwfJ7wfbHr6p5a2hnW1gtQlI4ODk7N2A2LzWDvet8wPZPRkRHPl9CQUQlMTthe4+ko7RhXYGEeJhzsuJaVXRjWHzIwtNwfYddhqLCwcpTTpGimMhvsuVzv/djYpBgWJvLydxlgptVirdZbX1ASFZUaXtOb4xOZX1OZHxNa4ZRX21DSV5gaG9Je6lqsepstO1knclcfJxtoc5tpNFuptVup9ZnkbdgjrVss+xjpuBvrd9snspOW29jdI5LVmlkj7Vvrd54t+RlfptQXXJWZHtlf51oruNgmMFfdJBYZn1RXnRWZXthfZxSeZiGgYGOhYLdxb/RubWZlpWFd3T////2kwjgAAAARXRSTlMAASFrcAhxIjLb/vWvsPb+20b4+DFFyMkz2vf43CP9/m5y9vZysLGvsQlw9fZs/v7c+PjcR8nK9/hI2/72sbD1/t00bXBAFktiAAAAAWJLR0SnwLcrAwAAAAd0SU1FB+YGCRchHQhxJNoAAAD/SURBVBjTY2BgYGBkYmZhZWVjZmdkgAAOTi5uHl4+fgEOqICgkKubu7uHp7AgiMcoIirm5e3j4+Pr5y8uKMHIwCQpFRAYFOzjExIaFi4tI8vALBcRGRUd4+MTGxefkCivwKColJSckpoGVJGekZmlrMLAqpqdk5uX7+NTUFhUXKLGysCqXlpWXlFZVV1TW1ffoKHJoKXd2NTc0trW3tHZ1d2jo8Kgq+fj09vXP2HCxEmTfXz0FRjYDQyn+EydNmHC9Bk+M42MZRkYJUxMZ82e0z933vwFZoIcYO+Imi9ctHjJ0mUWllC/iFhZ29ja2Ts4OkEFGNmdFTVZXRRkQeoBhkE/Yj5NSZ4AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjItMDYtMDlUMjM6MzM6MjgrMDA6MDA90JbEAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIyLTA2LTA5VDIzOjMzOjI4KzAwOjAwTI0ueAAAAABJRU5ErkJggg==";
var fav_icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB8lBMVEUAAAAAAAAAAAAAAAABAAAAAAABAAAAAAAAAAAdEBB0Pz5rKCgaBwcZBwdkMzJxPDocDAwAAACLTU6SOzsAAAAAAAA9Mic/LyEAAAA6HByQUUaIVEY+GBgAAACAQkKaQUIAAAABAQGWXl9+NjYBAAAaEBAcCAgZDQ0WBQUAAAB3Nzd9MjIAAACTUVK7UVJRNTWhVVaeVldTJSUAAAA+LC0+GhuGcmCgf2EAAABUTESrl4NzYlEdGhcdGhdiXFbIqIhWRjcAAAAAAAABAQGUSkq1VVX6bW6oUVGXS0vmro7+uJn6c3T6dXX/yqPnu5F3aFhxYVG/oH/7gHv6enjeuJOEcFtzX01VLCs4ODk7NmA5MTH1gHr6e3hWSTxHPl9CQUQ/JCKPYGGko7RhXYGEeJjmcW9cVnFjWH3IwtOHb3CjXV3CwcpTTpGimMjlb3D4c3RmYI1gWJvLydybZWW+T0x+V1hRP0Z7U1WTSEiHRUWGRUSORkZuTlBRQVBwX2CvRkXtaGjvamrNYWKmU1PVZ2fXaGjbaWncaWnAX1+7W1vkYF/ja2zRZWV9QkGeVFN2Pz69XV3ia2zkeHmpWFd/REOJSUirWVjjaGjBYGCeUlKMSkl8QkGBRUSoVlWeUE2QgXeWiHr1zqjmw5+bl5KVe2T///8NZLRGAAAARHRSTlMAASFrcAhxIjLb/vWvsPb+20b4+DFFyMkz2vf43CP9/m5y9vZysLGvsQn19mz+/tz4+NxHycr3+Ejb/vaxsPX+3TRtcBrzrrgAAAABYktHRKUuuUovAAAAB3RJTUUH5gYJFzsfVlK/LQAAAP9JREFUGNNjYGBgYGRiZmFlZWNmZ2SAAA5OLm4eXj5+AQ6ogKCQi6ubm7uHsCCIxygiKubp5e3t7ePrJy4owcjAJCnlHxAY5O0dHBIaJi0jy8AsFx4RGRXt7R0TGxefIK/AoKiUmJSckgpUkZaekamswsCqmpWdk5vn7Z1fUFhUrMbKwKpeUlpWXlFZVV1TW1evocnAotXQ2NTc0trW3tHZ2KWtwqCj6+3d3dPb19c/YaK3t54CA7u+wSTvyVP6+qZO855uaCTLwChhbDJj5qzZc6bOnWcqyAH2jqjZ/AULFy1eYm4B9YuIpZW1ja2dvYMjVICR3UlRk9VZQRakHgAlRz6K4dvoSgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMi0wNi0wOVQyMzo1OTozMSswMDowMJt1iQMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjItMDYtMDlUMjM6NTk6MzErMDA6MDDqKDG/AAAAAElFTkSuQmCC"
var favicon = {
// Change the Page Icon and Title.
change: function(iconURL) {
this.addLink(iconURL, "icon");
this.addLink(iconURL, "shortcut icon");
},
addLink: function(iconURL, relValue) {
var link = document.createElement("link");
link.type = "image/x-icon";
link.rel = relValue;
link.href = iconURL;
this.removeLink(relValue);
this.docHead.appendChild(link);
},
removeLink: function(relValue) {
var links = this.docHead.getElementsByTagName("link");
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (link.type == "image/x-icon" && link.rel == relValue) {
this.docHead.removeChild(link);
return; // Assuming only one match at most.
}
}
},
swapLink: function() {
if (this.run == true) {
if (this.icon == 1) {
this.change(fav_icon2);
this.icon = 2;
} else {
this.change(fav_icon1);
this.icon = 1;
}
}
},
auto_swap: function() {
if (this.run == true) {
this.swapLink();
setTimeout(() => { this.auto_swap(); }, 1000);
}
},
start_swap: function() {
this.run = true;
this.auto_swap();
},
stop_swap: function() {
this.run = false;
this.change(fav_icon);
},
docHead:document.getElementsByTagName("head")[0]
}

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" href="static/bootstrap.min.css">
<link rel="stylesheet" href="static/bootstrap-toggle.min.css">
<link rel="stylesheet" href="static/open-iconic-bootstrap.min.css">
<link rel="stylesheet" href="static/custom.css?ver=1.18.1a">
<link rel="stylesheet" href="static/custom.css?ver=1.18.1b">
<script src="static/jquery-3.6.0.min.js"></script>
<script src="static/jquery-ui.sortable.min.js"></script>
@ -17,7 +17,8 @@
<script src="static/bootstrap.min.js"></script>
<script src="static/bootstrap-toggle.min.js"></script>
<script src="static/rangy-core.min.js"></script>
<script src="static/application.js?ver=1.18.1a"></script>
<script src="static/application.js?ver=1.18.1b"></script>
<script src="static/favicon.js"></script>
</head>
<body>
<input type="file" id="remote-save-select" accept="application/json" style="display:none">
@ -33,6 +34,15 @@
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="nav navbar-nav">
{% if not hide_ai_menu %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">AI</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" id="btn_loadmodel">Load Model</a>
<a class="dropdown-item" href="#" id="btn_showmodel">Model Info</a>
</div>
</li>
{% endif %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">New Game</a>
<div class="dropdown-menu">
@ -86,6 +96,7 @@
</div>
<div id="connectstatusdiv" class="flex-row-container">
<span id="connectstatus" class="color_orange flex-row">Waiting for connection...</span>
<div class="layer-container status-container flex-push-left" style="color: #FFFFFF;" id="runtime"></div>
<div class="layer-container status-container flex-push-right">
<span class="oi oi-puzzle-piece statusicon layer-bottom" aria-hidden="true">
<div class="statustext statustext-wide">
@ -252,7 +263,7 @@
</div>
</div>
<div class="popupcontainer hidden" id="loadcontainer">
<div id="loadpopup">
<div class="loadpopup" id="loadpopup">
<div class="popuptitlebar">
<div class="popuptitletext">Select A Story To Load</div>
</div>
@ -268,6 +279,59 @@
</div>
</div>
</div>
<div class="popupcontainer hidden" id="loadmodelcontainer">
<div class="loadpopup">
<div class="popuptitlebar">
<div class="popuptitletext">Select A Model To Load</div>
</div>
<div id="loadmodellistbreadcrumbs">
</div>
<div id="loadmodellistcontent" style="overflow: scroll; height: 300px;">
</div>
<div class="popupfooter">
<input class="form-control hidden" type="text" placeholder="key" id="modelkey" onblur="socket.send({'cmd': 'OAI_Key_Update', 'key': $('#modelkey')[0].value});">
<input class="form-control hidden" type="text" placeholder="Enter the URL of the server (For example a trycloudflare link)" id="modelurl" onchange="check_enable_model_load()">
<input class="form-control hidden" type="text" placeholder="Model Path or Hugging Face Name" id="custommodelname" menu="" onblur="socket.send({'cmd': 'selectmodel', 'data': $(this).attr('menu'), 'path_modelname': $('#custommodelname')[0].value});">
</div>
<div class="popupfooter">
<select class="form-control hidden" id="oaimodel"><option value="">Select OAI Model</option></select>
</div>
<div class="popupfooter hidden" id=modellayers>
<div class='settingitem' style="width:100%">
<div class='settinglabel'>
<div class="justifyleft">
GPU Layers
<span class="helpicon">?
<span class="helptext">Number of layers to assign to the GPU</span>
</span>
</div>
<div class="justifyright" id="gpu_layers_current">0</div>
</div>
<div id=model_layer_bars style="color: white">
</div>
<input type=hidden id='gpu_count' value=0/>
<div class="settingminmax">
<div class="justifyleft">
0
</div>
<div class="justifyright" id="gpu_layers_max">
24
</div>
</div>
</div>
</div>
<div class="popupfooter">
<button type="button" class="btn btn-primary" id="btn_loadmodelaccept">Load</button>
<button type="button" class="btn btn-primary" id="btn_loadmodelclose">Cancel</button>
<div class="box flex-push-right hidden" id=use_gpu_div>
<input type="checkbox" data-toggle="toggle" data-onstyle="success" id="use_gpu" checked>
<div class="box-label">Use GPU</div>
</div>
</div>
</div>
</div>
<div class="popupcontainer hidden" id="spcontainer">
<div id="sppopup">
<div class="popuptitlebar">
@ -367,6 +431,19 @@
</div>
</div>
</div>
<div class="popupcontainer hidden flex" id="showmodelnamecontainer" style="center;">
<div class="loadpopup">
<div class="popuptitlebar" style="width:50% center;">
<div class="popuptitletext">Model Info</div>
</div>
<div id=showmodelnamecontent style="width:50%;">
Read Only
</div>
<div class="popupfooter" style="width:50% center;">
<button type="button" class="btn btn-primary" onclick='$("#showmodelnamecontainer").addClass("hidden");'>OK</button>
</div>
</div>
</div>
<div class="popupcontainer hidden" id="rndgamecontainer">
<div id="rspopup">
<div class="popuptitlebar">

220
test_aiserver.py Normal file
View File

@ -0,0 +1,220 @@
import pytest, time
import aiserver
#Test Model List:
test_models = [
('EleutherAI/gpt-neo-1.3B', {'key': False, 'gpu': False, 'layer_count': 24, 'breakmodel': True, 'url': False}),
('gpt2', {'key': False, 'gpu': False, 'layer_count': 12, 'breakmodel': True, 'url': False}),
('facebook/opt-350m', {'key': False, 'gpu': False, 'layer_count': 24, 'breakmodel': True, 'url': False})
]
@pytest.fixture
def client_data():
app = aiserver.app
#app.test_client_class = FlaskLoginClient
client_conn = app.test_client()
socketio_client = aiserver.socketio.test_client(app, flask_test_client=client_conn)
#Clear out the connection message
response = socketio_client.get_received()
return (client_conn, app, socketio_client)
def get_model_menu(model):
for menu in aiserver.model_menu:
for item in aiserver.model_menu[menu]:
if item[1] == model:
for main_menu_line in aiserver.model_menu['mainmenu']:
if main_menu_line[1] == menu:
return (menu, main_menu_line, item)
return None
def generate_story_data(client_data):
(client, app, socketio_client) = client_data
socketio_client.emit('message',{'cmd': 'submit', 'allowabort': False, 'actionmode': 0, 'chatname': None, 'data': ''})
#wait until the game state turns back to start
state = 'wait'
new_text = None
start_time = time.time()
timeout = time.time() + 60*1
while state == 'wait':
if time.time() > timeout:
break
responses = socketio_client.get_received()
for response in responses:
response = response['args'][0]
print(response)
if response['cmd'] == 'setgamestate':
state = response['data']
elif response['cmd'] == 'updatechunk' or response['cmd'] == 'genseqs':
new_text = response['data']
time.sleep(0.1)
assert new_text is not None
def test_basic_connection(client_data):
(client, app, socketio_client) = client_data
response = client.get("/")
assert response.status_code == 200
def test_load_story_from_web_ui(client_data):
(client, app, socketio_client) = client_data
#List out the stories and make sure we have the sample story
socketio_client.emit('message',{'cmd': 'loadlistrequest', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]['data']
found_sample_story = False
for story in response:
if story['name'] == 'sample_story':
found_sample_story = True
assert found_sample_story
#Click on the sample story, then click load
socketio_client.emit('message',{'cmd': 'loadselect', 'data': 'sample_story'})
socketio_client.emit('message',{'cmd': 'loadrequest', 'data': ''})
#Wait until we get the data back from the load
loaded_story = False
timeout = time.time() + 60*2
while not loaded_story:
if time.time() > timeout:
break
responses = socketio_client.get_received()
for response in responses:
response = response['args'][0]
if 'cmd' not in response:
print(response)
assert False
if response['cmd'] == 'updatescreen':
loaded_story = True
story_text = response['data']
break
assert loaded_story
#Verify that it's the right story data
assert story_text == '<chunk n="0" id="n0" tabindex="-1">Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking</chunk><chunk n="1" id="n1" tabindex="-1"> chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to</chunk><chunk n="2" id="n2" tabindex="-1"> do in a city? All that Niko needed to know was</chunk><chunk n="3" id="n3" tabindex="-1"> where to find the chicken and then how to make off with it.<br/><br/>A soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,</chunk>'
@pytest.mark.parametrize("model, expected_load_options", test_models)
def test_load_model_from_web_ui(client_data, model, expected_load_options):
(client, app, socketio_client) = client_data
#Clear out any old messages
response = socketio_client.get_received()
(menu, menu_line, model_line) = get_model_menu(model)
#Send the ai load model menu option
socketio_client.emit('message',{'cmd': 'list_model', 'data': 'mainmenu'})
response = socketio_client.get_received()[0]['args'][0]['data']
assert menu_line in response
#Send the click model menu option
socketio_client.emit('message',{'cmd': 'list_model', 'data': menu, 'pretty_name': ""})
response = socketio_client.get_received()[0]['args'][0]['data']
assert model_line in response
#Click the model
socketio_client.emit('message',{'cmd': 'selectmodel', 'data': model})
response = socketio_client.get_received()[0]['args'][0]
#Check that we're getting the right load options
print(response)
assert response['key'] == expected_load_options['key']
assert response['gpu'] == expected_load_options['gpu']
assert response['layer_count'] == expected_load_options['layer_count']
assert response['breakmodel'] == expected_load_options['breakmodel']
assert response['url'] == expected_load_options['url']
#Now send the load
socketio_client.emit('message',{'cmd': 'load_model', 'use_gpu': True, 'key': '', 'gpu_layers': '', 'url': '', 'online_model': ''})
#wait until the game state turns back to start
state = 'wait'
start_time = time.time()
timeout = time.time() + 60*2
while state == 'wait':
if time.time() > timeout:
break
responses = socketio_client.get_received()
for response in responses:
response = response['args'][0]
if response['cmd'] == 'setgamestate':
state = response['data']
time.sleep(0.1)
#Give it a second to get all of the settings, etc and clear out the messages
responses = socketio_client.get_received()
#check the model info to see if it's loaded
socketio_client.emit('message',{'cmd': 'show_model', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'show_model_name', 'data': model}
generate_story_data(client_data)
def test_load_GooseAI_from_web_ui(client_data):
pytest.skip("unsupported configuration")
@pytest.mark.parametrize("model, expected_load_options", test_models)
def test_load_model_from_command_line(client_data, model, expected_load_options):
(client, app, socketio_client) = client_data
#Clear out any old messages
response = socketio_client.get_received()
(menu, menu_line, model_line) = get_model_menu(model)
aiserver.general_startup("--model {}".format(model))
aiserver.load_model(initial_load=True)
#check the model info to see if it's loaded
socketio_client.emit('message',{'cmd': 'show_model', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'show_model_name', 'data': model}
generate_story_data(client_data)
def test_back_redo(client_data):
(client, app, socketio_client) = client_data
#Make sure we have known story in the ui
test_load_story_from_web_ui(client_data)
#Clear out any old messages
response = socketio_client.get_received()
#run a back action
socketio_client.emit('message',{'cmd': 'back', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'removechunk', 'data': 3}
#Run a redo action
socketio_client.emit('message',{'cmd': 'redo', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'updatechunk', 'data': {'index': 3, 'html': '<chunk n="3" id="n3" tabindex="-1"> where to find the chicken and then how to make off with it.<br/><br/>A soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,</chunk>'}}
#Go all the way back, then all the way forward
socketio_client.emit('message',{'cmd': 'back', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'removechunk', 'data': 3}
socketio_client.emit('message',{'cmd': 'back', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'removechunk', 'data': 2}
socketio_client.emit('message',{'cmd': 'back', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'removechunk', 'data': 1}
socketio_client.emit('message',{'cmd': 'back', 'data': ''})
response = socketio_client.get_received()[0]['args'][0]
assert response == {'cmd': 'errmsg', 'data': 'Cannot delete the prompt.'}
socketio_client.emit('message',{'cmd': 'redo', 'data': ''})
socketio_client.emit('message',{'cmd': 'redo', 'data': ''})
socketio_client.emit('message',{'cmd': 'redo', 'data': ''})
response = socketio_client.get_received()
assert response == [{'name': 'from_server', 'args': [{'cmd': 'updatescreen', 'gamestarted': True, 'data': '<chunk n="0" id="n0" tabindex="-1">Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking</chunk><chunk n="1" id="n1" tabindex="-1"> chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to</chunk>'}], 'namespace': '/'}, {'name': 'from_server', 'args': [{'cmd': 'texteffect', 'data': 1}], 'namespace': '/'}, {'name': 'from_server', 'args': [{'cmd': 'updatechunk', 'data': {'index': 2, 'html': '<chunk n="2" id="n2" tabindex="-1"> do in a city? All that Niko needed to know was</chunk>'}}], 'namespace': '/'}, {'name': 'from_server', 'args': [{'cmd': 'texteffect', 'data': 2}], 'namespace': '/'}, {'name': 'from_server', 'args': [{'cmd': 'updatechunk', 'data': {'index': 3, 'html': '<chunk n="3" id="n3" tabindex="-1"> where to find the chicken and then how to make off with it.<br/><br/>A soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,</chunk>'}}], 'namespace': '/'}, {'name': 'from_server', 'args': [{'cmd': 'texteffect', 'data': 3}], 'namespace': '/'}]

View File

@ -151,7 +151,7 @@ def decodenewlines(txt):
# Returns number of layers given an HF model config
#==================================================================#
def num_layers(config):
return config.num_layers if hasattr(config, "num_layers") else config.n_layer if hasattr(config, "n_layer") else config.num_hidden_layers
return config.num_layers if hasattr(config, "num_layers") else config.n_layer if hasattr(config, "n_layer") else config.num_hidden_layers if hasattr(config, 'num_hidden_layers') else None
#==================================================================#
# Downloads huggingface checkpoints using aria2c if possible