From c0736a8ec74ed0434db67463f269e6c2adf86ac2 Mon Sep 17 00:00:00 2001 From: KoboldAI Dev Date: Thu, 13 May 2021 01:26:42 -0400 Subject: [PATCH] Added World Info Added additional punctuation triggers for Add Sentence Spacing format Added better screen reset logic when refresing screen or restarting server --- aiserver.py | 215 ++++++++++++++++++++++++++++++++++++++---- static/application.js | 149 +++++++++++++++++++++++++++-- static/custom.css | 51 ++++++++++ templates/index.html | 3 + utils.py | 2 +- 5 files changed, 393 insertions(+), 27 deletions(-) diff --git a/aiserver.py b/aiserver.py index 35dc91de..07f2ec55 100644 --- a/aiserver.py +++ b/aiserver.py @@ -64,6 +64,8 @@ class vars: authornote = "" andepth = 3 # How far back in history to append author's note actions = [] + worldinfo = [] + deletewi = -1 # Temporary storage for index to delete mode = "play" # Whether the interface is in play, memory, or edit mode editln = 0 # Which line was last selected in Edit Mode url = "https://api.inferkit.com/v1/models/standard/generate" # InferKit API URL @@ -254,11 +256,14 @@ def do_connect(): setStartState() sendsettings() refresh_settings() + sendwi() + vars.mode = "play" else: # Game in session, send current game data and ready state to browser refresh_story() sendsettings() refresh_settings() + sendwi() if(vars.mode == "play"): if(not vars.aibusy): emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) @@ -268,6 +273,8 @@ def do_connect(): emit('from_server', {'cmd': 'editmode', 'data': 'true'}) elif(vars.mode == "memory"): emit('from_server', {'cmd': 'memmode', 'data': 'true'}) + elif(vars.mode == "wi"): + emit('from_server', {'cmd': 'wimode', 'data': 'true'}) #==================================================================# # Event triggered when browser SocketIO sends data to the server @@ -382,7 +389,17 @@ def get_message(msg): elif(msg['cmd'] == 'importaccept'): emit('from_server', {'cmd': 'popupshow', 'data': False}) importgame() - + elif(msg['cmd'] == 'wi'): + togglewimode() + elif(msg['cmd'] == 'wiinit'): + if(int(msg['data']) < len(vars.worldinfo)): + vars.worldinfo[msg['data']]["init"] = True + addwiitem() + elif(msg['cmd'] == 'widelete'): + deletewi(msg['data']) + elif(msg['cmd'] == 'sendwilist'): + commitwi(msg['data']) + #==================================================================# # #==================================================================# @@ -508,6 +525,15 @@ def calcsubmit(txt): anoteadded = False # In case our budget runs out before we hit A.N. depth actionlen = len(vars.actions) + # Scan for WorldInfo matches + winfo = checkworldinfo(txt) + + # Add a newline to the end of memory + if(vars.memory != "" and vars.memory[-1] != "\n"): + mem = vars.memory + "\n" + else: + mem = vars.memory + # Build Author's Note if set if(vars.authornote != ""): anotetxt = "\n[Author's note: "+vars.authornote+"]\n" @@ -520,19 +546,22 @@ def calcsubmit(txt): prompttkns = tokenizer.encode(vars.prompt) lnprompt = len(prompttkns) - memtokens = tokenizer.encode(vars.memory) + memtokens = tokenizer.encode(mem) lnmem = len(memtokens) + witokens = tokenizer.encode(winfo) + lnwi = len(witokens) + if(anotetxt != ""): anotetkns = tokenizer.encode(anotetxt) lnanote = len(anotetkns) - budget = vars.max_length - lnprompt - lnmem - lnanote - vars.genamt + budget = vars.max_length - lnprompt - lnmem - lnanote - lnwi - vars.genamt if(actionlen == 0): # First/Prompt action - subtxt = vars.memory + anotetxt + vars.prompt - lnsub = lnmem + lnprompt + lnanote + subtxt = vars.memory + winfo + anotetxt + vars.prompt + lnsub = lnmem + lnwi + lnprompt + lnanote generate(subtxt, lnsub+1, lnsub+vars.genamt) else: @@ -570,8 +599,8 @@ def calcsubmit(txt): else: tokens = memtokens + prompttkns + tokens else: - # Prepend Memory and Prompt before action tokens - tokens = memtokens + prompttkns + tokens + # Prepend Memory, WI, and Prompt before action tokens + tokens = memtokens + witokens + prompttkns + tokens # Send completed bundle to generator ln = len(tokens) @@ -587,7 +616,7 @@ def calcsubmit(txt): if(anotetxt != "" and actionlen < vars.andepth): forceanote = True - budget = vars.ikmax - len(vars.prompt) - len(anotetxt) - len(vars.memory) - 1 + budget = vars.ikmax - len(vars.prompt) - len(anotetxt) - len(mem) - len(winfo) - 1 subtxt = "" for n in range(actionlen): @@ -608,19 +637,14 @@ def calcsubmit(txt): subtxt = anotetxt + subtxt # A.N. len already taken from bdgt anoteadded = True - # Format memory for inclusion (adding newline separator) - memsub = "" - if(vars.memory != ""): - memsub = vars.memory + "\n" - # Did we get to add the A.N.? If not, do it here if(anotetxt != ""): if((not anoteadded) or forceanote): - subtxt = memsub + anotetxt + vars.prompt + subtxt + subtxt = mem + winfo + anotetxt + vars.prompt + subtxt else: - subtxt = memsub + vars.prompt + subtxt + subtxt = mem + winfo + vars.prompt + subtxt else: - subtxt = memsub + vars.prompt + subtxt + subtxt = mem + winfo + vars.prompt + subtxt # Send it! ikrequest(subtxt) @@ -812,6 +836,124 @@ def togglememorymode(): vars.mode = "play" emit('from_server', {'cmd': 'memmode', 'data': 'false'}) +#==================================================================# +# Toggles the game mode for WI editing and sends UI commands +#==================================================================# +def togglewimode(): + if(vars.mode == "play"): + vars.mode = "wi" + emit('from_server', {'cmd': 'wimode', 'data': 'true'}) + elif(vars.mode == "wi"): + # Commit WI fields first + requestwi() + # Then set UI state back to Play + vars.mode = "play" + emit('from_server', {'cmd': 'wimode', 'data': 'false'}) + +#==================================================================# +# +#==================================================================# +def addwiitem(): + ob = {"key": "", "content": "", "num": len(vars.worldinfo), "init": False} + vars.worldinfo.append(ob); + emit('from_server', {'cmd': 'addwiitem', 'data': ob}) + +#==================================================================# +# +#==================================================================# +def sendwi(): + # Cache len of WI + ln = len(vars.worldinfo) + + # Clear contents of WI container + emit('from_server', {'cmd': 'clearwi', 'data': ''}) + + # If there are no WI entries, send an empty WI object + if(ln == 0): + addwiitem() + else: + # Send contents of WI array + for wi in vars.worldinfo: + ob = wi + emit('from_server', {'cmd': 'addwiitem', 'data': ob}) + # Make sure last WI item is uninitialized + if(vars.worldinfo[-1]["init"]): + addwiitem() + +#==================================================================# +# Request current contents of all WI HTML elements +#==================================================================# +def requestwi(): + list = [] + for wi in vars.worldinfo: + list.append(wi["num"]) + emit('from_server', {'cmd': 'requestwiitem', 'data': list}) + +#==================================================================# +# Renumber WI items consecutively +#==================================================================# +def organizewi(): + if(len(vars.worldinfo) > 0): + count = 0 + for wi in vars.worldinfo: + wi["num"] = count + count += 1 + + +#==================================================================# +# Extract object from server and send it to WI objects +#==================================================================# +def commitwi(ar): + for ob in ar: + vars.worldinfo[ob["num"]]["key"] = ob["key"] + vars.worldinfo[ob["num"]]["content"] = ob["content"] + # Was this a deletion request? If so, remove the requested index + if(vars.deletewi >= 0): + del vars.worldinfo[vars.deletewi] + organizewi() + # Send the new WI array structure + sendwi() + # And reset deletewi index + vars.deletewi = -1 + +#==================================================================# +# +#==================================================================# +def deletewi(num): + if(num < len(vars.worldinfo)): + # Store index of deletion request + vars.deletewi = num + # Get contents of WI HTML inputs + requestwi() + +#==================================================================# +# Look for WI keys in text to generator +#==================================================================# +def checkworldinfo(txt): + # Dont go any further if WI is empty + if(len(vars.worldinfo) == 0): + return + + # Join submitted text to last action + if(len(vars.actions) > 0): + txt = vars.actions[-1] + txt + + # Scan text for matches on WI keys + wimem = "" + for wi in vars.worldinfo: + if(wi["key"] != ""): + # Split comma-separated keys + keys = wi["key"].split(",") + for k in keys: + # Remove leading/trailing spaces + ky = k.strip() + if ky in txt: + wimem = wimem + wi["content"] + "\n" + break + + return wimem + + #==================================================================# # Commit changes to Memory storage #==================================================================# @@ -892,6 +1034,8 @@ def exitModes(): emit('from_server', {'cmd': 'editmode', 'data': 'false'}) elif(vars.mode == "memory"): emit('from_server', {'cmd': 'memmode', 'data': 'false'}) + elif(vars.mode == "wi"): + emit('from_server', {'cmd': 'wimode', 'data': 'false'}) vars.mode = "play" #==================================================================# @@ -914,6 +1058,15 @@ def saveRequest(): js["memory"] = vars.memory js["authorsnote"] = vars.authornote js["actions"] = vars.actions + js["worldinfo"] = [] + + # Extract only the important bits of WI + for wi in vars.worldinfo: + if(wi["key"] != ""): + js["worldinfo"].append({ + "key": wi["key"], + "content": wi["content"] + }) # Write it file = open(savpath, "w") @@ -941,6 +1094,7 @@ def loadRequest(): vars.prompt = js["prompt"] vars.memory = js["memory"] vars.actions = js["actions"] + vars.worldinfo = [] # Try not to break older save files if("authorsnote" in js): @@ -948,9 +1102,21 @@ def loadRequest(): else: vars.authornote = "" + if("worldinfo" in js): + num = 0 + for wi in js["worldinfo"]: + vars.worldinfo.append({ + "key": wi["key"], + "content": wi["content"], + "num": num, + "init": True + }) + num += 1 + file.close() # Refresh game screen + sendwi() refresh_story() emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) @@ -1008,16 +1174,30 @@ def importgame(): vars.memory = ref["memory"] vars.authornote = ref["authorsNote"] vars.actions = [] + vars.worldinfo = [] # Get all actions except for prompt if(len(ref["actions"]) > 1): for act in ref["actions"][1:]: vars.actions.append(act["text"]) + # Get just the important parts of world info + if(len(ref["worldInfo"]) > 1): + num = 0 + for wi in ref["worldInfo"]: + vars.worldinfo.append({ + "key": wi["keys"], + "content": wi["entry"], + "num": num, + "init": True + }) + num += 1 + # Clear import data vars.importjs = {} # Refresh game screen + sendwi() refresh_story() emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) @@ -1056,4 +1236,5 @@ if __name__ == "__main__": # Start Flask/SocketIO (Blocking, so this must be last method!) print("{0}Server started!\rYou may now connect with a browser at http://127.0.0.1:5000/{1}".format(colors.GREEN, colors.END)) - socketio.run(app) + #socketio.run(app, host='0.0.0.0', port=5000) + socketio.run(app) \ No newline at end of file diff --git a/static/application.js b/static/application.js index e0d602c5..fdcaf7e7 100644 --- a/static/application.js +++ b/static/application.js @@ -19,11 +19,13 @@ var button_actmem; var button_actback; var button_actretry; var button_delete; +var button_actwi; var game_text; var input_text; var message_text; var settings_menu; var format_menu; +var wi_menu; var anote_menu; var anote_input; var anote_labelcur; @@ -120,6 +122,77 @@ function addImportLine(ob) { }); } +function addWiLine(ob) { + if(ob.init) { + wi_menu.append("
\ +
\ + \ + \ + \ +
\ +
\ + \ +
\ +
\ + \ +
\ +
"); + // Send key value to text input + $("#wikey"+ob.num).val(ob.key); + // Assign delete event to button + $("#btn_wi"+ob.num).on("click", function () { + showWiDeleteConfirm(ob.num); + }); + } else { + // Show WI line item with form fields hidden (uninitialized) + wi_menu.append("
\ +
\ + \ + \ + \ +
\ +
\ + \ +
\ +
\ + \ +
\ +
"); + // Assign function to expand WI item to button + $("#btn_wi"+ob.num).on("click", function () { + expandWiLine(ob.num); + }); + } + // Assign actions to other elements + $("#btn_wican"+ob.num).on("click", function () { + hideWiDeleteConfirm(ob.num); + }); + $("#btn_widel"+ob.num).on("click", function () { + socket.send({'cmd': 'widelete', 'data': ob.num}); + }); +} + +function expandWiLine(num) { + show([$("#wikey"+num), $("#wientry"+num)]); + $("#btn_wi"+num).html("X"); + $("#btn_wi"+num).off(); + // Tell server the WI entry was initialized + socket.send({'cmd': 'wiinit', 'data': num}); + $("#btn_wi"+num).on("click", function () { + showWiDeleteConfirm(num); + }); +} + +function showWiDeleteConfirm(num) { + hide([$("#btn_wi"+num)]); + show([$("#btn_widel"+num), $("#btn_wican"+num)]); +} + +function hideWiDeleteConfirm(num) { + show([$("#btn_wi"+num)]); + hide([$("#btn_widel"+num), $("#btn_wican"+num)]); +} + function highlightImportLine(ref) { $("#popupcontent > div").removeClass("popuplistselected"); ref.addClass("popuplistselected"); @@ -207,7 +280,7 @@ function enterEditMode() { editModeSelect($(this).attr("n")); }); disableSendBtn(); - hide([button_actback, button_actmem, button_actretry]); + hide([button_actback, button_actmem, button_actretry, button_actwi]); show([button_delete]); } @@ -218,7 +291,7 @@ function exitEditMode() { game_text.children('chunk').removeClass("chunkhov"); game_text.off('click', '> *'); enableSendBtn(); - show([button_actback, button_actmem, button_actretry]); + show([button_actback, button_actmem, button_actretry, button_actwi]); hide([button_delete]); input_text.val(""); } @@ -230,7 +303,7 @@ function editModeSelect(n) { function enterMemoryMode() { showMessage("Edit the memory to be sent with each request to the AI."); button_actmem.html("Cancel"); - hide([button_actback, button_actretry, button_actedit, button_delete]); + hide([button_actback, button_actretry, button_actedit, button_delete, button_actwi]); // Display Author's Note field anote_menu.slideDown("fast"); } @@ -238,12 +311,40 @@ function enterMemoryMode() { function exitMemoryMode() { hideMessage(); button_actmem.html("Memory"); - show([button_actback, button_actretry, button_actedit]); + show([button_actback, button_actretry, button_actedit, button_actwi]); input_text.val(""); // Hide Author's Note field anote_menu.slideUp("fast"); } +function enterWiMode() { + showMessage("World Info will be added to memory only when the key appears in submitted text or the last action."); + button_actwi.html("Accept"); + hide([button_actedit, button_actback, button_actmem, button_actretry, game_text]); + show([wi_menu]); + disableSendBtn(); +} + +function exitWiMode() { + hideMessage(); + button_actwi.html("W Info"); + hide([wi_menu]); + show([button_actedit, button_actback, button_actmem, button_actretry, game_text]); + enableSendBtn(); +} + +function returnWiList(ar) { + var list = []; + var i; + for(i=0; iConnected to KoboldAI Process!"); connect_status.removeClass("color_orange"); connect_status.addClass("color_green"); - // Reset Settings Menu + // Reset Menus settings_menu.html(""); format_menu.html(""); + wi_menu.html(""); } else if(msg.cmd == "updatescreen") { // Send game content to Game Screen game_text.html(msg.data); @@ -320,16 +425,22 @@ $(document).ready(function(){ // Enable or Disable buttons if(msg.data == "ready") { enableSendBtn(); - enableButtons([button_actedit, button_actmem, button_actback, button_actretry]); + enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); hideWaitAnimation(); } else if(msg.data == "wait") { disableSendBtn(); - disableButtons([button_actedit, button_actmem, button_actback, button_actretry]); + disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); showWaitAnimation(); } else if(msg.data == "start") { enableSendBtn(); - enableButtons([button_actmem]); + enableButtons([button_actmem, button_actwi]); disableButtons([button_actedit, button_actback, button_actretry]); + hide([wi_menu, button_delete]); + show([game_text, button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + hideMessage(); + button_actedit.html("Edit"); + button_actmem.html("Memory"); + button_actwi.html("W Info"); } } else if(msg.cmd == "editmode") { // Enable or Disable edit mode @@ -443,6 +554,22 @@ $(document).ready(function(){ } else if(msg.cmd == "clearpopup") { // Clear previous contents of popup popup_content.html(""); + } else if(msg.cmd == "wimode") { + // Enable or Disable WI edit mode + if(msg.data == "true") { + enterWiMode(); + } else { + exitWiMode(); + } + } else if(msg.cmd == "addwiitem") { + // Add WI entry to WI Menu + addWiLine(msg.data); + } else if(msg.cmd == "clearwi") { + // Clear previous contents of WI list + wi_menu.html(""); + } else if(msg.cmd == "requestwiitem") { + // Package WI contents and send back to server + returnWiList(msg.data); } }); @@ -509,6 +636,10 @@ $(document).ready(function(){ socket.send({'cmd': 'importaccept', 'data': ''}); }); + button_actwi.on("click", function(ev) { + socket.send({'cmd': 'wi', 'data': ''}); + }); + // I think this was removed? //$("#btn_savesettings").on("click", function(ev) { // socket.send({'cmd': 'savesettings', 'data': ''}); diff --git a/static/custom.css b/static/custom.css index 2c74481f..73e3392d 100644 --- a/static/custom.css +++ b/static/custom.css @@ -199,6 +199,13 @@ chunk { } +#wimenu { + max-height: 100%; + width: 100%; +} + +/*================= Classes =================*/ + .anotelabel { font-size: 10pt; color: #ffffff; @@ -263,6 +270,14 @@ chunk { display: none; } +.heightfull { + height: 100%; +} + +.heighthalf { + height: 50%; +} + .helpicon { display: inline-block; font-family: sans-serif; @@ -365,4 +380,40 @@ chunk { .spacer { display: inline-block; width: 50px; +} + +.wilistitem { + height: 80px; + display: grid; + grid-template-columns: 4% 30% 66%; + margin-bottom: 10px; +} + +.wientry { + padding-left: 10px; + background-color: #212122; +} + +.wientry > textarea { + height: 100%; + resize: none; + overflow:auto; + background-color: #404040; + color: #ffffff; +} + +.wikey { + background-color: #212122; +} + +.wikey > input { + height: 100%; + background-color: #404040; + color: #ffffff; +} + +.wiremove > button { + width: 80%; + overflow: hidden; + font-size: 12pt; } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 81854f68..4c4bc1a8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -38,12 +38,15 @@
... +
+ diff --git a/utils.py b/utils.py index eb532f31..10d4270f 100644 --- a/utils.py +++ b/utils.py @@ -69,7 +69,7 @@ def removespecialchars(txt): def addsentencespacing(txt, acts): # Get last character of last action lastchar = acts[-1][-1] - if(lastchar == "." or lastchar == "!" or lastchar == "?"): + if(lastchar == "." or lastchar == "!" or lastchar == "?" or lastchar == "," or lastchar == ";" or lastchar == ":"): txt = " " + txt return txt