From 2527d8db097cee8fa9bc4ea92c932bb9f87a2a16 Mon Sep 17 00:00:00 2001 From: ebolam Date: Sat, 13 Aug 2022 21:49:25 -0400 Subject: [PATCH] Soft Prompts working Update to make file browser more generic --- aiserver.py | 96 +++++++++++++++--- fileops.py | 2 +- koboldai_settings.py | 19 ++-- static/koboldai.css | 41 ++++---- static/koboldai.js | 173 ++++++++++++++++++++++----------- templates/popups.html | 1 + templates/settings flyout.html | 43 ++++++-- 7 files changed, 273 insertions(+), 102 deletions(-) diff --git a/aiserver.py b/aiserver.py index 773f8550..551a5bd4 100644 --- a/aiserver.py +++ b/aiserver.py @@ -841,6 +841,7 @@ def spRequest(filename): raise RuntimeError(f"{repr(filename)} is not a valid soft prompt file") with z.open('meta.json') as f: koboldai_vars.spmeta = json.load(f) + koboldai_vars.spname = koboldai_vars.spmeta['name'] z.close() with np.load(fileops.sppath(filename), allow_pickle=False) as f: @@ -1156,7 +1157,7 @@ def get_oai_models(data): file.write(json.dumps(js, indent=3), room="UI_1") emit('from_server', {'cmd': 'oai_engines', 'data': engines, 'online_model': online_model}, broadcast=True, room="UI_1") - emit('oai_engines', {'data': engines, 'online_model': online_model}, room="UI_2") + emit('oai_engines', {'data': engines, 'online_model': online_model}, broadcast=False, room="UI_2") else: # Something went wrong, print the message and quit since we can't initialize an engine print("{0}ERROR!{1}".format(colors.RED, colors.END), room="UI_1") @@ -2941,7 +2942,7 @@ def lua_set_spfilename(filename: Union[str, None]): filename = str(filename).strip() changed = lua_get_spfilename() != filename assert all(q not in filename for q in ("/", "\\")) - spRequest(filename) + spRequest("softprompts/"+filename) return changed #==================================================================# @@ -3435,7 +3436,7 @@ def get_message(msg): elif(msg['cmd'] == 'loadrequest'): loadRequest(fileops.storypath(koboldai_vars.loadselect)) elif(msg['cmd'] == 'sprequest'): - spRequest(koboldai_vars.spselect) + spRequest("softprompts/"+koboldai_vars.spselect) elif(msg['cmd'] == 'deletestory'): deletesave(msg['data']) elif(msg['cmd'] == 'renamestory'): @@ -5965,7 +5966,7 @@ def final_startup(): file = open("settings/" + getmodelname().replace('/', '_') + ".settings", "r") js = json.load(file) if(koboldai_vars.allowsp and "softprompt" in js and type(js["softprompt"]) is str and all(q not in js["softprompt"] for q in ("..", ":")) and (len(js["softprompt"]) == 0 or all(js["softprompt"][0] not in q for q in ("/", "\\")))): - spRequest(js["softprompt"]) + spRequest("softprompts/"+js["softprompt"]) else: koboldai_vars.spfilename = "" file.close() @@ -6062,14 +6063,14 @@ def upload_file(data): print("Someone is trying to upload a file to your server. Blocked.") elif session['popup_jailed_dir'] is None: if os.path.exists(path): - emit("error_popup", "The file already exists. Please delete it or rename the file before uploading", room="UI_2"); + emit("error_popup", "The file already exists. Please delete it or rename the file before uploading", broadcast=False, room="UI_2"); else: with open(path, "wb") as f: f.write(data['data']) get_files_folders(session['current_folder']) elif session['popup_jailed_dir'] in session['current_folder']: if os.path.exists(path): - emit("error_popup", "The file already exists. Please delete it or rename the file before uploading", room="UI_2"); + emit("error_popup", "The file already exists. Please delete it or rename the file before uploading", broadcast=False, room="UI_2"); else: with open(path, "wb") as f: f.write(data['data']) @@ -6176,7 +6177,9 @@ def popup_change_file(data): def file_popup(popup_title, starting_folder, return_event, upload=True, jailed=True, folder_only=True, renameable=False, deleteable=False, editable=False, show_breadcrumbs=True, item_check=None, show_hidden=False, - valid_only=False, hide_extention=False): + valid_only=False, hide_extention=False, extra_parameter_function=None, + column_names=['File Name'], show_filename=True, + column_widths=["100%"]): #starting_folder = The folder we're going to get folders and/or items from #return_event = the socketio event that will be emitted when the load button is clicked #jailed = if set to true will look for the session variable jailed_folder and prevent navigation outside of that folder @@ -6197,11 +6200,15 @@ def file_popup(popup_title, starting_folder, return_event, upload=True, jailed=T session['popup_editable'] = editable session['popup_show_hidden'] = show_hidden session['popup_item_check'] = item_check + session['extra_parameter_function'] = extra_parameter_function + session['column_names'] = column_names session['popup_folder_only'] = folder_only session['popup_show_breadcrumbs'] = show_breadcrumbs session['upload'] = upload session['valid_only'] = valid_only session['hide_extention'] = hide_extention + session['show_filename'] = show_filename + session['column_widths'] = column_widths socketio.emit("load_popup", {"popup_title": popup_title, "call_back": return_event, "renameable": renameable, "deleteable": deleteable, "editable": editable, 'upload': upload}, broadcast=False, room="UI_2") socketio.emit("load_popup", {"popup_title": popup_title, "call_back": return_event, "renameable": renameable, "deleteable": deleteable, "editable": editable, 'upload': upload}, broadcast=True, room="UI_1") @@ -6212,11 +6219,15 @@ def get_files_folders(starting_folder): import stat session['current_folder'] = os.path.abspath(starting_folder).replace("\\", "/") item_check = session['popup_item_check'] + extra_parameter_function = session['extra_parameter_function'] show_breadcrumbs = session['popup_show_breadcrumbs'] show_hidden = session['popup_show_hidden'] folder_only = session['popup_folder_only'] valid_only = session['valid_only'] + column_names = session['column_names'] hide_extention = session['hide_extention'] + show_filename = session['show_filename'] + column_widths = session['column_widths'] if starting_folder == 'This PC': breadcrumbs = [['This PC', 'This PC']] @@ -6253,24 +6264,29 @@ def get_files_folders(starting_folder): valid_selection = True else: valid_selection = item_check(item_full_path) + if extra_parameter_function is None: + extra_parameters = [] + else: + extra_parameters = extra_parameter_function(item_full_path, item, valid_selection) if (show_hidden and hidden) or not hidden: if os.path.isdir(os.path.join(base_path, item)): - folders.append([True, item_full_path, item, valid_selection]) + folders.append([True, item_full_path, item, valid_selection, extra_parameters]) else: if hide_extention: item = ".".join(item.split(".")[:-1]) if valid_only: if valid_selection: - files.append([False, item_full_path, item, valid_selection]) + files.append([False, item_full_path, item, valid_selection, extra_parameters]) else: - files.append([False, item_full_path, item, valid_selection]) + files.append([False, item_full_path, item, valid_selection, extra_parameters]) items = folders if not folder_only: items += files - socketio.emit("popup_items", items, broadcast=False, include_self=True, room="UI_2") + #items is a list of [Folder True/False, full path, file/folder name, validity of item to load, [list of extra columns]] + socketio.emit("popup_items", {"items": items, "column_names": column_names, "show_filename": show_filename, "column_widths": column_widths}, broadcast=False, include_self=True, room="UI_2") socketio.emit("popup_items", items, broadcast=True, include_self=True, room="UI_1") if show_breadcrumbs: socketio.emit("popup_breadcrumbs", breadcrumbs, broadcast=False, room="UI_2") @@ -6484,7 +6500,21 @@ def UI_2_load_model(data): def UI_2_load_story_list(data): file_popup("Select Story to Load", "./stories", "load_story", upload=True, jailed=True, folder_only=False, renameable=True, deleteable=True, show_breadcrumbs=True, item_check=valid_story, - valid_only=True, hide_extention=True) + valid_only=True, hide_extention=True, extra_parameter_function=get_story_length, + column_names=['Story Name', 'Action Count'], + column_widths=['auto', '100px']) + +def get_story_length(item_full_path, item, valid_selection): + if not valid_selection: + return [""] + with open(item_full_path, "r") as f: + js = json.load(f) + if 'file_version' not in js: + return [len(js['actions'])] + if js['file_version'] == 1: + return [len(js['actions'])] + return [0 if js['actions']['action_count'] == -1 else js['actions']['action_count'] ] + def valid_story(file): if file.endswith(".json"): @@ -6602,6 +6632,48 @@ def my_except_hook(exctype, value, traceback): sys.excepthook = my_except_hook +#==================================================================# +# Event triggered when Softprompt is clicked +#==================================================================# +@socketio.on('load_softprompt_list') +def UI_2_load_softprompt_list(data): + if not koboldai_vars.allowsp: + socketio.emit("error", "Soft prompts are not supported by your current model/backend", broadcast=True, room="UI_2") + assert koboldai_vars.allowsp, "Soft prompts are not supported by your current model/backend" + file_popup("Select Softprompt to Load", "./softprompts", "load_softprompt", upload=True, jailed=True, folder_only=False, renameable=True, + deleteable=True, show_breadcrumbs=True, item_check=valid_softprompt, + valid_only=True, hide_extention=True, extra_parameter_function=get_softprompt_desc, + column_names=['Softprompt Name', 'Softprompt Description'], + show_filename=False, + column_widths=['150px', 'auto']) + +def valid_softprompt(file): + z, version, shape, fortran_order, dtype = fileops.checksp(file, koboldai_vars.modeldim) + if z in [1, 2, 3, 4]: + return False + elif not isinstance(z, zipfile.ZipFile): + print("not zip") + return False + else: + return True + +def get_softprompt_desc(item_full_path, item, valid_selection): + if not valid_selection: + return [None, None] + z = zipfile.ZipFile(item_full_path) + with z.open('meta.json') as f: + ob = json.load(f) + return [ob['name'], ob['description']] + + +#==================================================================# +# Event triggered when Softprompt is clicked +#==================================================================# +@socketio.on('load_softprompt') +def UI_2_load_softprompt(data): + print("Load softprompt: {}".format(data)) + spRequest(data) + #==================================================================# # Test #==================================================================# diff --git a/fileops.py b/fileops.py index d8dc552c..1982bbf0 100644 --- a/fileops.py +++ b/fileops.py @@ -119,7 +119,7 @@ def checksp(filename: str, model_dimension: int) -> Tuple[Union[zipfile.ZipFile, if 'np' not in globals(): import numpy as np try: - z = zipfile.ZipFile("softprompts/"+filename) + z = zipfile.ZipFile(filename) with z.open('tensor.npy') as f: # Read only the header of the npy file, for efficiency reasons version: Tuple[int, int] = np.lib.format.read_magic(f) diff --git a/koboldai_settings.py b/koboldai_settings.py index 84812a6d..fa3e1295 100644 --- a/koboldai_settings.py +++ b/koboldai_settings.py @@ -17,6 +17,15 @@ def clean_var_for_emit(value): else: return value +def create_loopback_socketio(): + sio = socketio_client.Client() + @sio.event + def connect(): + pass + sio.connect('ws://localhost:{}/?rely=true'.format(port)) + rely_clients[threading.get_ident()] = sio + return sio + def process_variable_changes(socketio, classname, name, value, old_value, debug_message=None): if serverstarted and name != "serverstarted": if debug_message is not None: @@ -37,13 +46,10 @@ def process_variable_changes(socketio, classname, name, value, old_value, debug_ if not has_request_context(): if threading.get_ident() in rely_clients: sio = rely_clients[threading.get_ident()] + if not sio.connected: + sio = create_loopback_socketio() else: - sio = socketio_client.Client() - @sio.event - def connect(): - pass - sio.connect('ws://localhost:{}/?rely=true'.format(port)) - rely_clients[threading.get_ident()] = sio + sio = create_loopback_socketio() #release no longer used clients for thread in rely_clients: if thread not in [x.ident for x in threading.enumerate()]: @@ -496,6 +502,7 @@ class system_settings(settings): self.usegpu = False # Whether to launch pipeline with GPU support self.spselect = "" # Temporary storage for soft prompt filename to load self.spmeta = None # Metadata of current soft prompt, or None if not using a soft prompt + self.spname = "Not in Use" # Name of the soft prompt self.sp = None # Current soft prompt tensor (as a NumPy array) self.sp_length = 0 # Length of current soft prompt in tokens, or 0 if not using a soft prompt self.has_genmod = False # Whether or not at least one loaded Lua userscript has a generation modifier diff --git a/static/koboldai.css b/static/koboldai.css index 9fdd5bdf..c599809b 100644 --- a/static/koboldai.css +++ b/static/koboldai.css @@ -176,7 +176,7 @@ grid-template-areas: "label" "item"; grid-template-rows: 20px 120px; - grid-template-columns: var(--flyout_menu_width); + grid-template-columns: 200px; row-gap: 0.2em; background-color: var(--setting_background); color: var(--text); @@ -933,7 +933,7 @@ body { top: 10vh; left: 10%; z-index: 999; - width: 80%; + width: 80vw; height: 80vh; background-color: black; display: flex; @@ -955,43 +955,50 @@ body { flex-basis: auto; } -.popup .item { +.popup .model_item { width: 100%; background-color: var(--popup_item_color); padding: 2px; display: grid; - grid-template-areas: "folder_icon delete_icon edit_icon rename_icon file"; - grid-template-columns: 20px 20px 20px 20px auto; + grid-template-areas: "folder_icon delete_icon edit_icon rename_icon file gpu_size"; + grid-template-columns: 30px 0px 0px 0px auto 50px; } -.popup .item .folder_icon { +.popup .model_item .folder_icon { grid-area: folder_icon; } -.popup .item .edit_icon { +.popup .model_item .edit_icon { grid-area: edit_icon; } -.popup .item .rename_icon { +.popup .model_item .rename_icon { grid-area: rename_icon; } -.popup .item .delete_icon { +.popup .model_item .delete_icon { grid-area: delete_icon; } -.popup .item .file { - grid-area: file; - display: grid; +.popup .model_item .model { cursor: pointer; + grid-area: file; } -.popup .item .file:hover { +.popup .model_item .model:hover { background-color: #688f1f; } -.popup .item .file.selected { +.popup .model_item .model.selected { + background: #688f1f; +} + +.popup .item:hover { + background-color: #688f1f; +} + +.popup .item.selected { background: #688f1f; } @@ -1024,7 +1031,7 @@ body { } /*----------------------------- Model Load Popup ------------------------------------------*/ -.popup .item .model { +.popup_list_area .model_item .model { grid-area: file; display: grid; grid-template-areas: "item gpu_size"; @@ -1032,11 +1039,11 @@ body { cursor: pointer; } -.popup .item .model:hover { +.popup_list_area .model_item .model:hover { background-color: #688f1f; } -.popup .item .model.selected { +.popup_list_area .model_item .model.selected { background-color: #688f1f; } diff --git a/static/koboldai.js b/static/koboldai.js index 7762aa85..ef0223c0 100644 --- a/static/koboldai.js +++ b/static/koboldai.js @@ -375,6 +375,7 @@ function do_ai_busy(data) { } function var_changed(data) { + //console.log({"name": data.name, "data": data}); //Special Case for Story Text if ((data.classname == "actions") && (data.name == "Selected Text")) { do_story_text_updates(data); @@ -526,6 +527,7 @@ function load_popup(data) { } function popup_items(data) { + console.log(data); var popup_list = document.getElementById('popup_list'); //first, let's clear out our existing data while (popup_list.firstChild) { @@ -533,9 +535,45 @@ function popup_items(data) { } document.getElementById('popup_upload_input').value = ""; - for (item of data) { - var list_item = document.createElement("span"); - list_item.classList.add("item"); + //create the column widths + var style = 'width: 80vw; display: grid; grid-template-areas: "icons'; + for (i=0; i < data.column_widths.length; i++) { + style = style + " p"+i; + } + style = style + '"; grid-template-columns: 30px'; + for (column_width of data.column_widths) { + style = style + " "+column_width; + } + style = style + ';'; + + //create titles + var tr = document.createElement("div"); + tr.style = style; + //icon area + var td = document.createElement("span"); + td.style = "grid-area: icons;"; + tr.append(td) + + //add dynamic columns + var i = 0; + for (column of data.column_names) { + td = document.createElement("span"); + td.textContent = column; + td.style = "grid-area: p"+i+";"; + i+=1; + tr.append(td) + } + popup_list.append(tr); + + //create lines + for (item of data.items) { + var tr = document.createElement("div"); + tr.classList.add("item"); + tr.setAttribute("folder", item[0]); + tr.setAttribute("valid", item[3]); + tr.style = style; + var icon_area = document.createElement("span"); + icon_area.style = "grid-area: icons;"; //create the folder icon var folder_icon = document.createElement("span"); @@ -544,7 +582,7 @@ function popup_items(data) { folder_icon.classList.add("oi"); folder_icon.setAttribute('data-glyph', "folder"); } - list_item.append(folder_icon); + icon_area.append(folder_icon); //create the edit icon var edit_icon = document.createElement("span"); @@ -558,7 +596,7 @@ function popup_items(data) { socket.emit("popup_edit", this.id); }; } - list_item.append(edit_icon); + icon_area.append(edit_icon); //create the rename icon var rename_icon = document.createElement("span"); @@ -576,7 +614,7 @@ function popup_items(data) { } }; } - list_item.append(rename_icon); + icon_area.append(rename_icon); //create the delete icon var delete_icon = document.createElement("span"); @@ -599,40 +637,76 @@ function popup_items(data) { } }; } - list_item.append(delete_icon); + icon_area.append(delete_icon); + tr.append(icon_area); //create the actual item - var popup_item = document.createElement("span"); - popup_item.classList.add("file"); - popup_item.id = item[1]; - popup_item.setAttribute("folder", item[0]); - popup_item.setAttribute("valid", item[3]); - popup_item.textContent = item[2]; - popup_item.onclick = function () { - var accept = document.getElementById("popup_accept"); - if (this.getAttribute("valid") == "true") { - accept.classList.remove("disabled"); - accept.setAttribute("selected_value", this.id); - } else { - accept.setAttribute("selected_value", ""); - accept.classList.add("disabled"); - if (this.getAttribute("folder") == "true") { - socket.emit("popup_change_folder", this.id); + i=0; + if (data.show_filename) { + var popup_item = document.createElement("span"); + popup_item.style = "grid-area: p"+i+";"; + i+=1; + popup_item.id = item[1]; + popup_item.setAttribute("folder", item[0]); + popup_item.setAttribute("valid", item[3]); + popup_item.textContent = item[2]; + popup_item.onclick = function () { + var accept = document.getElementById("popup_accept"); + if (this.getAttribute("valid") == "true") { + accept.classList.remove("disabled"); + accept.setAttribute("selected_value", this.id); + } else { + accept.setAttribute("selected_value", ""); + accept.classList.add("disabled"); + if (this.getAttribute("folder") == "true") { + socket.emit("popup_change_folder", this.id); + } } - } - var popup_list = document.getElementById('popup_list').getElementsByClassName("selected"); - for (item of popup_list) { - item.classList.remove("selected"); - } - this.classList.add("selected"); - }; - list_item.append(popup_item); + var popup_list = document.getElementById('popup_list').getElementsByClassName("selected"); + for (item of popup_list) { + item.classList.remove("selected"); + } + this.parentElement.classList.add("selected"); + }; + tr.append(popup_item); + } + + for (extra_data of item[4]) { + td = document.createElement("span"); + td.style = "grid-area: p"+i+";"; + i+=1; + td.id = item[1]; + td.setAttribute("folder", item[0]); + td.setAttribute("valid", item[3]); + td.textContent = extra_data; + td.onclick = function () { + var accept = document.getElementById("popup_accept"); + if (this.getAttribute("valid") == "true") { + accept.classList.remove("disabled"); + accept.setAttribute("selected_value", this.id); + } else { + accept.setAttribute("selected_value", ""); + accept.classList.add("disabled"); + if (this.getAttribute("folder") == "true") { + socket.emit("popup_change_folder", this.id); + } + } + var popup_list = document.getElementById('popup_list').getElementsByClassName("selected"); + for (item of popup_list) { + item.classList.remove("selected"); + } + this.classList.add("selected"); + }; + tr.append(td); + } - popup_list.append(list_item); + popup_list.append(tr); } + + } function popup_breadcrumbs(data) { @@ -749,38 +823,19 @@ function show_model_menu(data) { //add items for (item of data.data) { var list_item = document.createElement("span"); - list_item.classList.add("item"); + list_item.classList.add("model_item"); //create the folder icon var folder_icon = document.createElement("span"); - folder_icon.classList.add("folder_icon"); - if (item[3]) { - folder_icon.classList.add("oi"); - folder_icon.setAttribute('data-glyph', "folder"); + folder_icon.classList.add("material-icons-outlined"); + folder_icon.classList.add("cursor"); + if ((item[3]) || (item[0] == 'Load a model from its directory') || (item[0] == 'Load an old GPT-2 model (eg CloverEdition)')) { + folder_icon.textContent = "folder"; + } else { + folder_icon.textContent = "psychology"; } list_item.append(folder_icon); - //create the delete icon - //var delete_icon = document.createElement("span"); - //delete_icon.classList.add("delete_icon"); - //if (popup_deleteable) { - // delete_icon.classList.add("oi"); - // delete_icon.setAttribute('data-glyph', "x"); - // delete_icon.id = item[1]; - // delete_icon.setAttribute("folder", item[0]); - // delete_icon.onclick = function () { - // if (this.getAttribute("folder") == "true") { - // if (window.confirm("Do you really want to delete this folder and ALL files under it?")) { - // socket.emit("popup_delete", this.id); - // } - // } else { - // if (window.confirm("Do you really want to delete this file?")) { - // socket.emit("popup_delete", this.id); - // } - // } - // }; - //} - //list_item.append(delete_icon); //create the actual item var popup_item = document.createElement("span"); @@ -1919,7 +1974,7 @@ function update_token_lengths() { } //figure out prompt length if ((document.getElementById("story_prompt").getAttribute("story_prompt_length") == null) || (document.getElementById("story_prompt").getAttribute("story_prompt_length") == "")) { - prompt_length = 999999999999; + prompt_length = 0; } else { prompt_length = parseInt(document.getElementById("story_prompt").getAttribute("story_prompt_length")); } diff --git a/templates/popups.html b/templates/popups.html index 83a81261..63a33cea 100644 --- a/templates/popups.html +++ b/templates/popups.html @@ -4,6 +4,7 @@ Popup Title + {% endwith %}
@@ -162,13 +181,16 @@