From 9559347f82529f43a0a2f239b7830722c66b93f3 Mon Sep 17 00:00:00 2001 From: Javalar <39832337+Javalar@users.noreply.github.com> Date: Tue, 15 Jun 2021 00:59:08 -0400 Subject: [PATCH 001/117] Update or remove targeted chunks in Game Screen (#2) --- .gitignore | 8 +++++- aiserver.py | 59 +++++++++++++++++++++++++++++++++---------- static/application.js | 33 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index ff83d6e7..33d3c719 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,10 @@ client.settings # Ignore stories file except for test_story stories/* -!stories/sample_story.json \ No newline at end of file +!stories/sample_story.json + +# Ignore PyCharm project files. +.idea + +# Ignore compiled Python files. +*.pyc diff --git a/aiserver.py b/aiserver.py index 8dcec2af..c6b27672 100644 --- a/aiserver.py +++ b/aiserver.py @@ -9,6 +9,8 @@ from os import path, getcwd import tkinter as tk from tkinter import messagebox import json +from typing import Literal, Union + import requests import html @@ -666,12 +668,12 @@ def actionsubmit(data): data = applyinputformatting(data) # Store the result in the Action log vars.actions.append(data) - + update_story_chunk('last') + if(not vars.noai): # Off to the tokenizer! calcsubmit(data) else: - refresh_story() set_aibusy(0) #==================================================================# @@ -687,7 +689,7 @@ def actionretry(data): # Remove last action if possible and resubmit if(len(vars.actions) > 0): vars.actions.pop() - refresh_story() + remove_story_chunk(len(vars.actions) + 1) calcsubmit('') #==================================================================# @@ -698,8 +700,9 @@ def actionback(): return # Remove last index of actions and refresh game screen if(len(vars.actions) > 0): + action_index = len(vars.actions) vars.actions.pop() - refresh_story() + remove_story_chunk(len(vars.actions) + 1) #==================================================================# # Take submitted text and build the text to be given to generator @@ -936,7 +939,7 @@ def genresult(genout): # Add formatted text to Actions array and refresh the game screen vars.actions.append(genout) - refresh_story() + update_story_chunk('last') emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) #==================================================================# @@ -955,9 +958,6 @@ def genselect(genout): # Send sequences to UI for selection emit('from_server', {'cmd': 'genseqs', 'data': genout}) - - # Refresh story for any input text - refresh_story() #==================================================================# # Send selected sequence to action log and refresh UI @@ -966,7 +966,7 @@ def selectsequence(n): if(len(vars.genseqs) == 0): return vars.actions.append(vars.genseqs[int(n)]["generated_text"]) - refresh_story() + update_story_chunk('last') emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}) vars.genseqs = [] @@ -1096,6 +1096,39 @@ def refresh_story(): text_parts.extend(('', html.escape(item), '')) emit('from_server', {'cmd': 'updatescreen', 'data': formatforhtml(''.join(text_parts))}) + +#==================================================================# +# Signals the Game Screen to update one of the chunks +#==================================================================# +def update_story_chunk(idx: Union[int, Literal['last']]): + if idx == 'last': + if len(vars.actions) <= 1: + # In this case, we are better off just refreshing the whole thing as the + # prompt might not have been shown yet (with a "Generating story..." + # messsage instead). + refresh_story() + return + + idx = len(vars.actions) + + if idx == 0: + text = vars.prompt + else: + # Actions are 0 based, but in chunks 0 is the prompt. + # So the chunk index is one more than the corresponding action index. + text = vars.actions[idx - 1] + + chunk_text = f'{formatforhtml(html.escape(text))}' + emit('from_server', {'cmd': 'updatechunk', 'data': {'index': idx, 'html': chunk_text, 'last': (idx == len(vars.actions))}}) + + +#==================================================================# +# Signals the Game Screen to remove one of the chunks +#==================================================================# +def remove_story_chunk(idx: int): + emit('from_server', {'cmd': 'removechunk', 'data': idx}) + + #==================================================================# # Sends the current generator settings to the Game Menu #==================================================================# @@ -1161,7 +1194,7 @@ def editsubmit(data): vars.actions[vars.editln-1] = data vars.mode = "play" - refresh_story() + update_story_chunk(vars.editln) emit('from_server', {'cmd': 'texteffect', 'data': vars.editln}) emit('from_server', {'cmd': 'editmode', 'data': 'false'}) @@ -1176,7 +1209,7 @@ def deleterequest(): else: del vars.actions[vars.editln-1] vars.mode = "play" - refresh_story() + remove_story_chunk(vars.editln) emit('from_server', {'cmd': 'editmode', 'data': 'false'}) #==================================================================# @@ -1382,7 +1415,7 @@ def ikrequest(txt): genout = req.json()["data"]["text"] print("{0}{1}{2}".format(colors.CYAN, genout, colors.END)) vars.actions.append(genout) - refresh_story() + update_story_chunk('last') emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) set_aibusy(0) @@ -1432,7 +1465,7 @@ def oairequest(txt, min, max): genout = req.json()["choices"][0]["text"] print("{0}{1}{2}".format(colors.CYAN, genout, colors.END)) vars.actions.append(genout) - refresh_story() + update_story_chunk('last') emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) set_aibusy(0) diff --git a/static/application.js b/static/application.js index e5ebc806..2cf66dfe 100644 --- a/static/application.js +++ b/static/application.js @@ -599,6 +599,39 @@ $(document).ready(function(){ setTimeout(function () { $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); }, 5); + } else if(msg.cmd == "updatechunk") { + const {index, html, last} = msg.data; + const existingChunk = game_text.children(`#n${index}`) + const newChunk = $(html); + if (existingChunk.length > 0) { + // Update existing chunk + existingChunk.before(newChunk); + existingChunk.remove(); + } else { + // Append at the end + game_text.append(newChunk); + } + if(last) { + // Scroll to bottom of text if it's the last element + setTimeout(function () { + $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); + }, 5); + } + } else if(msg.cmd == "removechunk") { + let index = msg.data; + // Remove the chunk + game_text.children(`#n${index}`).remove() + // Shift all existing chunks by 1 + index++; + while (true) { + const chunk = game_text.children(`#n${index}`) + if(chunk.length === 0) { + break; + } + const newIndex = index - 1; + chunk.attr('n', newIndex.toString()).attr('id', `n${newIndex}`); + index++; + } } else if(msg.cmd == "setgamestate") { // Enable or Disable buttons if(msg.data == "ready") { From 81aba7cba8070426ae09361763caa634a325030e Mon Sep 17 00:00:00 2001 From: Yves Dubois Date: Tue, 15 Jun 2021 01:02:11 -0400 Subject: [PATCH 002/117] Fix typo --- aiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index c6b27672..6703fb53 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1105,7 +1105,7 @@ def update_story_chunk(idx: Union[int, Literal['last']]): if len(vars.actions) <= 1: # In this case, we are better off just refreshing the whole thing as the # prompt might not have been shown yet (with a "Generating story..." - # messsage instead). + # message instead). refresh_story() return From bbe3a92ce4804f9bef5de7a235b746f9ede7a922 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:02:19 -0500 Subject: [PATCH 003/117] Fixes for unusual story loading circumstances. This PR does three things when loading a story from within the browser: 1. Prevents an error if a story file is not valid JSON. 2. Catches an error is a file is JSON, but lacks an actions property. 3. Replaces getcwd() and instead uses the path of the script file itself in case someone does not start the app from the current working directory. --- fileops.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/fileops.py b/fileops.py index dc5cb66e..640d3083 100644 --- a/fileops.py +++ b/fileops.py @@ -59,14 +59,22 @@ def getdirpath(dir, title): #==================================================================# def getstoryfiles(): list = [] - for file in listdir(getcwd()+"/stories"): + for file in listdir(path.dirname(path.realpath(__file__))+"/stories"): if file.endswith(".json"): ob = {} ob["name"] = file.replace(".json", "") - f = open(getcwd()+"/stories/"+file, "r") - js = json.load(f) + f = open(path.dirname(path.realpath(__file__))+"/stories/"+file, "r") + try: + js = json.load(f) + except: + print("Browser loading error: Story file is malformed or not a JSON file.") + f.close() + continue f.close() - ob["actions"] = len(js["actions"]) + try: + ob["actions"] = len(js["actions"]) + except TypeError: + print("Browser loading error: Story file has incorrect format.") list.append(ob) return list @@ -74,4 +82,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(getcwd()+"/stories/"+name+".json") \ No newline at end of file + return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") \ No newline at end of file From f9db17025db3e4901fbab2db9d2cc3102b46cfb2 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:17:07 -0500 Subject: [PATCH 004/117] Ack! Forgot a continue statement. --- fileops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fileops.py b/fileops.py index 640d3083..b4c02329 100644 --- a/fileops.py +++ b/fileops.py @@ -75,6 +75,7 @@ def getstoryfiles(): ob["actions"] = len(js["actions"]) except TypeError: print("Browser loading error: Story file has incorrect format.") + continue list.append(ob) return list @@ -82,4 +83,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") \ No newline at end of file + return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") From ad39a4c8b3a8c010d1b1a8a25d7fd13cf1193366 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:18:37 -0500 Subject: [PATCH 005/117] Ack! Forgot a continue statement. --- fileops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fileops.py b/fileops.py index 640d3083..9a574537 100644 --- a/fileops.py +++ b/fileops.py @@ -75,6 +75,7 @@ def getstoryfiles(): ob["actions"] = len(js["actions"]) except TypeError: print("Browser loading error: Story file has incorrect format.") + continue list.append(ob) return list From d5522f0d0ad6a6eb432bf2e7489f216d65c73ddd Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:31:43 -0500 Subject: [PATCH 006/117] Yet another silly mistake. Sigh. --- fileops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fileops.py b/fileops.py index b4c02329..70fd79b0 100644 --- a/fileops.py +++ b/fileops.py @@ -83,4 +83,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") + return path.exists(path.dirname(path.realpath(__file__))+"/stories/"+name+".json") From 2a191c2d7c2b934226638a5efaa407008a670006 Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 01:24:01 +0200 Subject: [PATCH 007/117] Update readme.txt --- readme.txt | 82 ++---------------------------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/readme.txt b/readme.txt index 5e67a05d..7490f34e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,80 +1,2 @@ -Thanks for checking out the KoboldAI Client! Get support and updates on the subreddit: -https://www.reddit.com/r/KoboldAI/ - -[ABOUT] - -This is a browser-based front-end for AI-assisted writing with multiple local & remote AI models. -It offers the standard array of tools, including Memory, Author's Note, World Info, Save & Load, -adjustable AI settings, formatting options, and the ability to import exising AI Dungeon adventures. -Current UI Snapshot: https://imgur.com/mjk5Yre - -For local generation, KoboldAI uses Transformers (https://huggingface.co/transformers/) to interact -with the AI models. This can be done either on CPU, or GPU with sufficient hardware. If you have a -high-end GPU with sufficient VRAM to run your model of choice, see -(https://www.tensorflow.org/install/gpu) for instructions on enabling GPU support. - -Transformers/Tensorflow can still be used on CPU if you do not have high-end hardware, but generation -times will be much longer. Alternatively, KoboldAI also supports utilizing remotely-hosted models. -The currently supported remote APIs are InferKit and Google Colab, see the dedicated sections below -for more info on these. - -[SETUP] - -1. Install a 64-bit version of Python. - (Development was done on 3.7, I have not tested newer versions) - Windows download link: https://www.python.org/ftp/python/3.7.9/python-3.7.9-amd64.exe -2. When installing Python make sure "Add Python to PATH" is selected. - (If pip isn't working, run the installer again and choose Modify to choose Optional features.) -3. Run install_requirements.bat. - (This will install the necessary python packages via pip) -4. Run play.bat -5. Select a model from the list. Flask will start and give you a message that it's ready to connect. -6. Open a web browser and enter http://127.0.0.1:5000/ - -[ENABLE COLORS IN WINDOWS 10 COMMAND LINE] - -If you see strange numeric tags in the console output, then your console of choice does not have -color support enabled. On Windows 10, you can enable color support by lanching the registry editor -and adding the REG_DWORD key VirtualTerminalLevel to Computer\HKEY_CURRENT_USER\Console and setting -its value to 1. - -[ENABLE GPU FOR SUPPORTED VIDEO CARDS] - -1. Install NVidia CUDA toolkit from https://developer.nvidia.com/cuda-10.2-download-archive -2. Visit PyTorch's website(https://pytorch.org/get-started/locally/) and select Pip under "Package" -and your version of CUDA under "Compute Platform" (I linked 10.2) to get the pip3 command. -3. Copy and paste pip3 command into command prompt to install torch with GPU support - -Be aware that when using GPU mode, inference will be MUCH faster but if your GPU doesn't have enough -VRAM to load the model it will crash the application. - -[IMPORT AI DUNGEON GAMES] - -To import your games from AI Dungeon, first grab CuriousNekomimi's AI Dungeon Content Archive Toolkit: -https://github.com/CuriousNekomimi/AIDCAT -Follow the video instructions for getting your access_token, and run aidcat.py in command prompt. -Choose option [1] Download your saved content. -Choose option [2] Download your adventures. -Save the JSON file to your computer using the prompt. -Run KoboldAI, and after connecting to the web GUI, press the Import button at the top. -Navigate to the JSON file exported from AIDCAT and select it. A prompt will appear in the GUI -presenting you with all Adventures scraped from your AI Dungeon account. -Select an Adventure and click the Accept button. - -[HOST GPT-NEO ON GOOGLE COLAB] - -If your computer does not have an 8GB GPU to run GPT-Neo locally, you can now run a Google Colab -notebook hosting a GPT-Neo-2.7B model remotely and connect to it using the KoboldAI client. -See the instructions on the Colab at the link below: -https://colab.research.google.com/drive/1uGe9f4ruIQog3RLxfUsoThakvLpHjIkX?usp=sharing - -[FOR INFERKIT INTEGRATION] - -If you would like to use InferKit's Megatron-11b model, sign up for a free account on their website. -https://inferkit.com/ -After verifying your email address, sign in and click on your profile picture in the top right. -In the drop down menu, click "API Key". -On the API Key page, click "Reveal API Key" and copy it. When starting KoboldAI and selecting the -InferKit API model, you will be asked to paste your API key into the terminal. After entering, -the API key will be stored in the client.settings file for future use. -You can see your remaining budget for generated characters on their website under "Billing & Usage". \ No newline at end of file +This branch will eventually be used for a community edition of KoboldAI, uniting the different community made editions. +For now it is just a placeholder. From 17dce2e16cc7a921fcd0ffa9d7acb6653fa9bab0 Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 01:26:03 +0200 Subject: [PATCH 008/117] Update readme.txt --- readme.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.txt b/readme.txt index 7490f34e..5ea1e028 100644 --- a/readme.txt +++ b/readme.txt @@ -1,2 +1,6 @@ This branch will eventually be used for a community edition of KoboldAI, uniting the different community made editions. For now it is just a placeholder. + +--- + +Looking for ColabKobold? Check the different branches on my account. From efbe40f1f6fd130c381530acdaa435ef957d1771 Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 12:54:44 +0200 Subject: [PATCH 009/117] Random Story Generator Add the Random Story Generator and more userfriendly defaults --- .gitignore | 3 +- UPDATE YOUR COLAB NOTEBOOK.txt | 3 - aiserver.py | 23 +- gensettings.py | 18 +- readme.txt | 2 +- static/application - Copy.js | 981 +++++++++++++++++++++++++++++++++ static/application.js | 33 ++ static/application.js.bak | 981 +++++++++++++++++++++++++++++++++ static/custom.css | 6 + templates/index.html | 33 +- 10 files changed, 2058 insertions(+), 25 deletions(-) delete mode 100644 UPDATE YOUR COLAB NOTEBOOK.txt create mode 100644 static/application - Copy.js create mode 100644 static/application.js.bak diff --git a/.gitignore b/.gitignore index ff83d6e7..8a6b218c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ client.settings # Ignore stories file except for test_story stories/* -!stories/sample_story.json \ No newline at end of file +!stories/sample_story.json +/.project diff --git a/UPDATE YOUR COLAB NOTEBOOK.txt b/UPDATE YOUR COLAB NOTEBOOK.txt deleted file mode 100644 index 67a821f4..00000000 --- a/UPDATE YOUR COLAB NOTEBOOK.txt +++ /dev/null @@ -1,3 +0,0 @@ -If you use Google Colab to run your models, and you made a local copy of the Colab notebook in Google Drive instead of using the community notebook, you MUST make a new copy of the community notebook to use the new multiple-sequence generation feature. The link is below: - -https://colab.research.google.com/drive/1uGe9f4ruIQog3RLxfUsoThakvLpHjIkX?usp=sharing \ No newline at end of file diff --git a/aiserver.py b/aiserver.py index 8dcec2af..ba583480 100644 --- a/aiserver.py +++ b/aiserver.py @@ -56,13 +56,13 @@ class vars: model = "" # Model ID string chosen at startup noai = False # Runs the script without starting up the transformers pipeline aibusy = False # Stops submissions while the AI is working - max_length = 512 # Maximum number of tokens to submit per action + max_length = 1024 # Maximum number of tokens to submit per action ikmax = 3000 # Maximum number of characters to submit to InferKit - genamt = 60 # Amount of text for each action to generate + genamt = 80 # Amount of text for each action to generate ikgen = 200 # Number of characters for InferKit to generate - rep_pen = 1.0 # Default generator repetition_penalty - temp = 1.0 # Default generator temperature - top_p = 1.0 # Default generator top_p + rep_pen = 1.1 # Default generator repetition_penalty + temp = 0.5 # Default generator temperature + top_p = 0.9 # Default generator top_p numseqs = 1 # Number of sequences to ask the generator to create gamestarted = False # Whether the game has started (disables UI elements) prompt = "" # Prompt @@ -75,7 +75,7 @@ class vars: badwordsids = [] # Tokenized array of badwords deletewi = -1 # Temporary storage for index to delete wirmvwhtsp = False # Whether to remove leading whitespace from WI entries - widepth = 1 # How many historical actions to scan for WI hits + widepth = 3 # How many historical actions to scan for WI hits 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 @@ -434,6 +434,8 @@ def get_message(msg): importRequest() elif(msg['cmd'] == 'newgame'): newGameRequest() + elif(msg['cmd'] == 'rndgame'): + randomGameRequest(msg['data']) elif(msg['cmd'] == 'settemp'): vars.temp = float(msg['data']) emit('from_server', {'cmd': 'setlabeltemp', 'data': msg['data']}) @@ -1803,6 +1805,11 @@ def newGameRequest(): sendwi() setStartState() +def randomGameRequest(topic): + newGameRequest() + vars.memory = "You generate the following " + topic + " story concept :" + actionsubmit("") + vars.memory = "" #==================================================================# # Final startup commands to launch Flask app @@ -1813,5 +1820,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, host='0.0.0.0', port=5000) - socketio.run(app) \ No newline at end of file + socketio.run(app, host='0.0.0.0', port=5000) + #socketio.run(app) diff --git a/gensettings.py b/gensettings.py index 8f6a67a0..eb914139 100644 --- a/gensettings.py +++ b/gensettings.py @@ -6,7 +6,7 @@ gensettingstf = [{ "min": 0.1, "max": 2.0, "step": 0.05, - "default": 1.0, + "default": 0.5, "tooltip": "Randomness of sampling. High values can increase creativity but may make text less sensible. Lower values will make text more predictable but can become repetitious." }, { @@ -17,7 +17,7 @@ gensettingstf = [{ "min": 0.1, "max": 1.0, "step": 0.05, - "default": 1.0, + "default": 0.9, "tooltip": "Used to discard unlikely text in the sampling process. Lower values will make text more predictable but can become repetitious." }, { @@ -28,7 +28,7 @@ gensettingstf = [{ "min": 1.0, "max": 2.0, "step": 0.05, - "default": 1.0, + "default": 1.1, "tooltip": "Used to penalize words that were already generated or belong to the context." }, { @@ -39,7 +39,7 @@ gensettingstf = [{ "min": 16, "max": 512, "step": 2, - "default": 60, + "default": 80, "tooltip": "Number of tokens the AI should generate. Higher numbers will take longer to generate." }, { @@ -50,7 +50,7 @@ gensettingstf = [{ "min": 512, "max": 2048, "step": 8, - "default": 512, + "default": 1024, "tooltip": "Max number of tokens of context to submit to the AI for sampling. Make sure this is higher than Amount to Generate. Higher values increase VRAM/RAM usage." }, { @@ -72,7 +72,7 @@ gensettingstf = [{ "min": 1, "max": 5, "step": 1, - "default": 1, + "default": 3, "tooltip": "Number of historic actions to scan for W Info keys." }, { @@ -95,7 +95,7 @@ gensettingsik =[{ "min": 0.1, "max": 2.0, "step": 0.05, - "default": 1.0, + "default": 0.5, "tooltip": "Randomness of sampling. High values can increase creativity but may make text less sensible. Lower values will make text more predictable but can become repetitious." }, { @@ -106,7 +106,7 @@ gensettingsik =[{ "min": 0.1, "max": 1.0, "step": 0.05, - "default": 1.0, + "default": 1.1, "tooltip": "Used to discard unlikely text in the sampling process. Lower values will make text more predictable but can become repetitious." }, { @@ -128,7 +128,7 @@ gensettingsik =[{ "min": 1, "max": 5, "step": 1, - "default": 1, + "default": 3, "tooltip": "Number of historic actions to scan for W Info keys." }, { diff --git a/readme.txt b/readme.txt index 5ea1e028..9ab782bb 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ This branch will eventually be used for a community edition of KoboldAI, uniting the different community made editions. -For now it is just a placeholder. +For now it is a WIP branch with Random Story Generation and better default settings. --- diff --git a/static/application - Copy.js b/static/application - Copy.js new file mode 100644 index 00000000..71c066d4 --- /dev/null +++ b/static/application - Copy.js @@ -0,0 +1,981 @@ +//=================================================================// +// VARIABLES +//=================================================================// + +// Socket IO Object +var socket; + +// UI references for jQuery +var connect_status; +var button_newgame; +var button_rndgame; +var button_save; +var button_saveas; +var button_savetofile; +var button_load; +var button_import; +var button_importwi; +var button_impaidg; +var button_settings; +var button_format; +var button_send; +var button_actedit; +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; +var anote_slider; +var popup; +var popup_title; +var popup_content; +var popup_accept; +var popup_close; +var aidgpopup; +var aidgpromptnum; +var aidg_accept; +var aidg_close; +var saveaspopup; +var saveasinput; +var saveas_accept; +var saveas_close; +var saveasoverwrite; +var loadpopup; +var loadcontent; +var load_accept; +var load_close; +var nspopup; +var ns_accept; +var ns_close; +var rspopup; +var rs_accept; +var rs_close; +var seqselmenu; +var seqselcontents; + +// Key states +var shift_down = false; +var do_clear_ent = false; + +// Display vars +var allowtoggle = false; +var formatcount = 0; + +//=================================================================// +// METHODS +//=================================================================// + +function addSetting(ob) { + // Add setting block to Settings Menu + if(ob.uitype == "slider"){ + settings_menu.append("
\ +
\ +
\ + "+ob.label+" ?"+ob.tooltip+"\ +
\ +
\ + "+ob.default+"\ +
\ +
\ +
\ + \ +
\ +
\ +
\ + "+ob.min+"\ +
\ +
\ + "+ob.max+"\ +
\ +
\ +
"); + // Set references to HTML objects + var refin = $("#"+ob.id); + var reflb = $("#"+ob.id+"cur"); + window["setting_"+ob.id] = refin; // Is this still needed? + window["label_"+ob.id] = reflb; // Is this still needed? + // Add event function to input + refin.on("input", function () { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).val()}); + }); + } else if(ob.uitype == "toggle"){ + settings_menu.append("
\ + \ + "+ob.label+" \ + ?"+ob.tooltip+"\ +
"); + // Tell Bootstrap-Toggle to render the new checkbox + $("input[type=checkbox]").bootstrapToggle(); + $("#"+ob.id).on("change", function () { + if(allowtoggle) { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); + } + }); + } +} + +function addFormat(ob) { + // Check if we need to make a new column for this button + if(formatcount == 0) { + format_menu.append("
"); + } + // Get reference to the last child column + var ref = $("#formatmenu > div").last(); + // Add format block to Format Menu + ref.append("
\ + \ + "+ob.label+" \ + ?"+ob.tooltip+"\ +
"); + // Tell Bootstrap-Toggle to render the new checkbox + $("input[type=checkbox]").bootstrapToggle(); + // Add event to input + $("#"+ob.id).on("change", function () { + if(allowtoggle) { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); + } + }); + // Increment display variable + formatcount++; + if(formatcount == 2) { + formatcount = 0; + } +} + +function addImportLine(ob) { + popup_content.append("
\ +
"+ob.title+"
\ +
"+ob.acts+"
\ +
"+ob.descr+"
\ +
"); + $("#import"+ob.num).on("click", function () { + socket.send({'cmd': 'importselect', 'data': $(this).attr('id')}); + highlightImportLine($(this)); + }); +} + +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"); + enableButtons([popup_accept]); +} + +function enableButtons(refs) { + for(i=0; i"); +} + +function hideWaitAnimation() { + $('#waitanim').remove(); +} + +function hide(refs) { + for(i=0; i *', function() { + editModeSelect($(this).attr("n")); + }); + disableSendBtn(); + hide([button_actback, button_actmem, button_actretry, button_actwi]); + show([button_delete]); +} + +function exitEditMode() { + // Remove class to each story chunk + hideMessage(); + button_actedit.html("Edit"); + game_text.children('chunk').removeClass("chunkhov"); + game_text.off('click', '> *'); + enableSendBtn(); + show([button_actback, button_actmem, button_actretry, button_actwi]); + hide([button_delete]); + input_text.val(""); +} + +function editModeSelect(n) { + socket.send({'cmd': 'editline', 'data': 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, button_actwi]); + // Display Author's Note field + anote_menu.slideDown("fast"); +} + +function exitMemoryMode() { + hideMessage(); + button_actmem.html("Memory"); + 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; i\ +
"+ar[i].name+"
\ +
"+ar[i].actions+"
\ + "); + $("#load"+i).on("click", function () { + enableButtons([load_accept]); + socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")}); + highlightLoadLine($(this)); + }); + } +} + +function highlightLoadLine(ref) { + $("#loadlistcontent > div").removeClass("popuplistselected"); + ref.addClass("popuplistselected"); +} + +function showNewStoryPopup() { + nspopup.removeClass("hidden"); + nspopup.addClass("flex"); +} + +function hideNewStoryPopup() { + nspopup.removeClass("flex"); + nspopup.addClass("hidden"); +} + +function showRandomStoryPopup() { + rspopup.removeClass("hidden"); + rspopup.addClass("flex"); +} + +function hideRandomStoryPopup() { + rspopup.removeClass("flex"); + rspopup.addClass("hidden"); +} + +function setStartState() { + enableSendBtn(); + 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(); + hideWaitAnimation(); + button_actedit.html("Edit"); + button_actmem.html("Memory"); + button_actwi.html("W Info"); + hideAidgPopup(); + hideSaveAsPopup(); + hideLoadPopup(); + hideNewStoryPopup(); + hidegenseqs(); +} + +function parsegenseqs(seqs) { + seqselcontents.html(""); + var i; + for(i=0; i"+seqs[i].generated_text+""); + $("#seqsel"+i).on("click", function () { + socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")}); + }); + } + $('#seqselmenu').slideDown("slow"); +} + +function hidegenseqs() { + $('#seqselmenu').slideUp("slow", function() { + seqselcontents.html(""); + }); +} + +//=================================================================// +// READY/RUNTIME +//=================================================================// + +$(document).ready(function(){ + + // Bind UI references + connect_status = $('#connectstatus'); + button_newgame = $('#btn_newgame'); + button_rndgame = $('#btn_rndgame'); + button_save = $('#btn_save'); + button_saveas = $('#btn_saveas'); + button_savetofile = $('#btn_savetofile'); + button_load = $('#btn_load'); + button_loadfrfile = $('#btn_loadfromfile'); + button_import = $("#btn_import"); + button_importwi = $("#btn_importwi"); + button_impaidg = $("#btn_impaidg"); + button_settings = $('#btn_settings'); + button_format = $('#btn_format'); + button_send = $('#btnsend'); + button_actedit = $('#btn_actedit'); + button_actmem = $('#btn_actmem'); + button_actback = $('#btn_actundo'); + button_actretry = $('#btn_actretry'); + button_delete = $('#btn_delete'); + button_actwi = $('#btn_actwi'); + game_text = $('#gametext'); + input_text = $('#input_text'); + message_text = $('#messagefield'); + settings_menu = $("#settingsmenu"); + format_menu = $('#formatmenu'); + anote_menu = $('#anoterowcontainer'); + wi_menu = $('#wimenu'); + anote_input = $('#anoteinput'); + anote_labelcur = $('#anotecur'); + anote_slider = $('#anotedepth'); + popup = $("#popupcontainer"); + popup_title = $("#popuptitletext"); + popup_content = $("#popupcontent"); + popup_accept = $("#btn_popupaccept"); + popup_close = $("#btn_popupclose"); + aidgpopup = $("#aidgpopupcontainer"); + aidgpromptnum = $("#aidgpromptnum"); + aidg_accept = $("#btn_aidgpopupaccept"); + aidg_close = $("#btn_aidgpopupclose"); + saveaspopup = $("#saveascontainer"); + saveasinput = $("#savename"); + saveas_accept = $("#btn_saveasaccept"); + saveas_close = $("#btn_saveasclose"); + saveasoverwrite = $("#saveasoverwrite"); + loadpopup = $("#loadcontainer"); + loadcontent = $("#loadlistcontent"); + load_accept = $("#btn_loadaccept"); + load_close = $("#btn_loadclose"); + nspopup = $("#newgamecontainer"); + ns_accept = $("#btn_nsaccept"); + ns_close = $("#btn_nsclose"); + rspopup = $("#rndgamecontainer"); + rs_accept = $("#btn_rsaccept"); + rs_close = $("#btn_rsclose"); + seqselmenu = $("#seqselmenu"); + seqselcontents = $("#seqselcontents"); + + // Connect to SocketIO server + loc = window.document.location; + socket = io.connect(loc.href); + + socket.on('from_server', function(msg) { + if(msg.cmd == "connected") { + // Connected to Server Actions + connect_status.html("Connected to KoboldAI Process!"); + connect_status.removeClass("color_orange"); + connect_status.addClass("color_green"); + // 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); + // Scroll to bottom of text + setTimeout(function () { + $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); + }, 5); + } else if(msg.cmd == "setgamestate") { + // Enable or Disable buttons + if(msg.data == "ready") { + enableSendBtn(); + enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + hideWaitAnimation(); + } else if(msg.data == "wait") { + disableSendBtn(); + disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + showWaitAnimation(); + } else if(msg.data == "start") { + setStartState(); + } + } else if(msg.cmd == "editmode") { + // Enable or Disable edit mode + if(msg.data == "true") { + enterEditMode(); + } else { + exitEditMode(); + } + } else if(msg.cmd == "setinputtext") { + // Set input box text for edit mode + input_text.val(msg.data); + } else if(msg.cmd == "enablesubmit") { + // Enables the submit button + enableSendBtn(); + } else if(msg.cmd == "memmode") { + // Enable or Disable memory edit mode + if(msg.data == "true") { + enterMemoryMode(); + } else { + exitMemoryMode(); + } + } else if(msg.cmd == "errmsg") { + // Send error message + errMessage(msg.data); + } else if(msg.cmd == "texteffect") { + // Apply color highlight to line of text + newTextHighlight($("#n"+msg.data)) + } else if(msg.cmd == "updatetemp") { + // Send current temp value to input + $("#settemp").val(parseFloat(msg.data)); + $("#settempcur").html(msg.data); + } else if(msg.cmd == "updatetopp") { + // Send current top p value to input + $("#settopp").val(parseFloat(msg.data)); + $("#settoppcur").html(msg.data); + } else if(msg.cmd == "updatereppen") { + // Send current rep pen value to input + $("#setreppen").val(parseFloat(msg.data)); + $("#setreppencur").html(msg.data); + } else if(msg.cmd == "updateoutlen") { + // Send current output amt value to input + $("#setoutput").val(parseInt(msg.data)); + $("#setoutputcur").html(msg.data); + } else if(msg.cmd == "updatetknmax") { + // Send current max tokens value to input + $("#settknmax").val(parseInt(msg.data)); + $("#settknmaxcur").html(msg.data); + } else if(msg.cmd == "updateikgen") { + // Send current max tokens value to input + $("#setikgen").val(parseInt(msg.data)); + $("#setikgencur").html(msg.data); + } else if(msg.cmd == "setlabeltemp") { + // Update setting label with value from server + $("#settempcur").html(msg.data); + } else if(msg.cmd == "setlabeltopp") { + // Update setting label with value from server + $("#settoppcur").html(msg.data); + } else if(msg.cmd == "setlabelreppen") { + // Update setting label with value from server + $("#setreppencur").html(msg.data); + } else if(msg.cmd == "setlabeloutput") { + // Update setting label with value from server + $("#setoutputcur").html(msg.data); + } else if(msg.cmd == "setlabeltknmax") { + // Update setting label with value from server + $("#settknmaxcur").html(msg.data); + } else if(msg.cmd == "setlabelikgen") { + // Update setting label with value from server + $("#setikgencur").html(msg.data); + } else if(msg.cmd == "updateanotedepth") { + // Send current Author's Note depth value to input + anote_slider.val(parseInt(msg.data)); + anote_labelcur.html(msg.data); + } else if(msg.cmd == "setlabelanotedepth") { + // Update setting label with value from server + anote_labelcur.html(msg.data); + } else if(msg.cmd == "getanote") { + // Request contents of Author's Note field + var txt = anote_input.val(); + socket.send({'cmd': 'anote', 'data': txt}); + } else if(msg.cmd == "setanote") { + // Set contents of Author's Note field + anote_input.val(msg.data); + } else if(msg.cmd == "addsetting") { + // Add setting controls + addSetting(msg.data); + } else if(msg.cmd == "addformat") { + // Add setting controls + addFormat(msg.data); + } else if(msg.cmd == "updatefrmttriminc") { + // Update toggle state + $("#frmttriminc").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtrmblln") { + // Update toggle state + $("#frmtrmblln").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtrmspch") { + // Update toggle state + $("#frmtrmspch").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtadsnsp") { + // Update toggle state + $("#frmtadsnsp").prop('checked', msg.data).change(); + } else if(msg.cmd == "allowtoggle") { + // Allow toggle change states to propagate + allowtoggle = msg.data; + } else if(msg.cmd == "popupshow") { + // Show/Hide Popup + popupShow(msg.data); + } else if(msg.cmd == "addimportline") { + // Add import popup entry + addImportLine(msg.data); + } 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); + } else if(msg.cmd == "saveas") { + // Show Save As prompt + showSaveAsPopup(); + } else if(msg.cmd == "hidesaveas") { + // Hide Save As prompt + hideSaveAsPopup(); + } else if(msg.cmd == "buildload") { + // Send array of save files to load UI + buildLoadList(msg.data); + } else if(msg.cmd == "askforoverwrite") { + // Show overwrite warning + show([saveasoverwrite]); + } else if(msg.cmd == "genseqs") { + // Parse generator sequences to UI + parsegenseqs(msg.data); + } else if(msg.cmd == "hidegenseqs") { + // Collapse genseqs menu + hidegenseqs(); + } else if(msg.cmd == "setlabelnumseq") { + // Update setting label with value from server + $("#setnumseqcur").html(msg.data); + } else if(msg.cmd == "updatenumseq") { + // Send current max tokens value to input + $("#setnumseq").val(parseInt(msg.data)); + $("#setnumseqcur").html(msg.data); + } else if(msg.cmd == "setlabelwidepth") { + // Update setting label with value from server + $("#setwidepthcur").html(msg.data); + } else if(msg.cmd == "updatewidepth") { + // Send current max tokens value to input + $("#setwidepth").val(parseInt(msg.data)); + $("#setwidepthcur").html(msg.data); + } else if(msg.cmd == "updateuseprompt") { + // Update toggle state + $("#setuseprompt").prop('checked', msg.data).change(); + } + }); + + socket.on('disconnect', function() { + connect_status.html("Lost connection..."); + connect_status.removeClass("color_green"); + connect_status.addClass("color_orange"); + }); + + // Bind actions to UI buttons + button_send.on("click", function(ev) { + dosubmit(); + }); + + button_actretry.on("click", function(ev) { + socket.send({'cmd': 'retry', 'data': ''}); + hidegenseqs(); + }); + + button_actback.on("click", function(ev) { + socket.send({'cmd': 'back', 'data': ''}); + hidegenseqs(); + }); + + button_actedit.on("click", function(ev) { + socket.send({'cmd': 'edit', 'data': ''}); + }); + + button_delete.on("click", function(ev) { + socket.send({'cmd': 'delete', 'data': ''}); + }); + + button_actmem.on("click", function(ev) { + socket.send({'cmd': 'memory', 'data': ''}); + }); + + button_savetofile.on("click", function(ev) { + socket.send({'cmd': 'savetofile', 'data': ''}); + }); + + button_loadfrfile.on("click", function(ev) { + socket.send({'cmd': 'loadfromfile', 'data': ''}); + }); + + button_import.on("click", function(ev) { + socket.send({'cmd': 'import', 'data': ''}); + }); + + button_importwi.on("click", function(ev) { + socket.send({'cmd': 'importwi', 'data': ''}); + }); + + button_settings.on("click", function(ev) { + $('#settingsmenu').slideToggle("slow"); + }); + + button_format.on("click", function(ev) { + $('#formatmenu').slideToggle("slow"); + }); + + popup_close.on("click", function(ev) { + socket.send({'cmd': 'importcancel', 'data': ''}); + }); + + popup_accept.on("click", function(ev) { + socket.send({'cmd': 'importaccept', 'data': ''}); + }); + + button_actwi.on("click", function(ev) { + socket.send({'cmd': 'wi', 'data': ''}); + }); + + button_impaidg.on("click", function(ev) { + showAidgPopup(); + }); + + aidg_close.on("click", function(ev) { + hideAidgPopup(); + }); + + aidg_accept.on("click", function(ev) { + sendAidgImportRequest(); + }); + + button_save.on("click", function(ev) { + socket.send({'cmd': 'saverequest', 'data': ''}); + }); + + button_saveas.on("click", function(ev) { + showSaveAsPopup(); + }); + + saveas_close.on("click", function(ev) { + hideSaveAsPopup(); + socket.send({'cmd': 'clearoverwrite', 'data': ''}); + }); + + saveas_accept.on("click", function(ev) { + sendSaveAsRequest(); + }); + + button_load.on("click", function(ev) { + socket.send({'cmd': 'loadlistrequest', 'data': ''}); + }); + + load_close.on("click", function(ev) { + hideLoadPopup(); + }); + + load_accept.on("click", function(ev) { + socket.send({'cmd': 'loadrequest', 'data': ''}); + hideLoadPopup(); + }); + + button_newgame.on("click", function(ev) { + showNewStoryPopup(); + }); + + ns_accept.on("click", function(ev) { + socket.send({'cmd': 'newgame', 'data': ''}); + hideNewStoryPopup(); + }); + + ns_close.on("click", function(ev) { + hideNewStoryPopup(); + }); + + button_rndgame.on("click", function(ev) { + showRandomStoryPopup(); + }); + + rs_accept.on("click", function(ev) { + socket.send({'cmd': 'rndgame', 'data': ''}); + hideRandomStoryPopup(); + }); + + rs_close.on("click", function(ev) { + hideRandomStoryPopup(); + }); + + anote_slider.on("input", function () { + socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); + }); + + saveasinput.on("input", function () { + if(saveasinput.val() == "") { + disableButtons([saveas_accept]); + } else { + enableButtons([saveas_accept]); + } + hide([saveasoverwrite]); + }); + + // Bind Enter button to submit + input_text.keydown(function (ev) { + if (ev.which == 13 && !shift_down) { + do_clear_ent = true; + dosubmit(); + } else if(ev.which == 16) { + shift_down = true; + } + }); + + // Enter to submit, but not if holding shift + input_text.keyup(function (ev) { + if (ev.which == 13 && do_clear_ent) { + input_text.val(""); + do_clear_ent = false; + } else if(ev.which == 16) { + shift_down = false; + } + }); + + aidgpromptnum.keydown(function (ev) { + if (ev.which == 13) { + sendAidgImportRequest(); + } + }); + + saveasinput.keydown(function (ev) { + if (ev.which == 13 && saveasinput.val() != "") { + sendSaveAsRequest(); + } + }); +}); + diff --git a/static/application.js b/static/application.js index e5ebc806..5a4a0086 100644 --- a/static/application.js +++ b/static/application.js @@ -8,6 +8,7 @@ var socket; // UI references for jQuery var connect_status; var button_newgame; +var button_rndgame; var button_save; var button_saveas; var button_savetofile; @@ -45,6 +46,7 @@ var aidg_accept; var aidg_close; var saveaspopup; var saveasinput; +var topic; var saveas_accept; var saveas_close; var saveasoverwrite; @@ -55,6 +57,9 @@ var load_close; var nspopup; var ns_accept; var ns_close; +var rspopup; +var rs_accept; +var rs_close; var seqselmenu; var seqselcontents; @@ -482,6 +487,16 @@ function hideNewStoryPopup() { nspopup.addClass("hidden"); } +function showRandomStoryPopup() { + rspopup.removeClass("hidden"); + rspopup.addClass("flex"); +} + +function hideRandomStoryPopup() { + rspopup.removeClass("flex"); + rspopup.addClass("hidden"); +} + function setStartState() { enableSendBtn(); enableButtons([button_actmem, button_actwi]); @@ -527,6 +542,7 @@ $(document).ready(function(){ // Bind UI references connect_status = $('#connectstatus'); button_newgame = $('#btn_newgame'); + button_rndgame = $('#btn_rndgame'); button_save = $('#btn_save'); button_saveas = $('#btn_saveas'); button_savetofile = $('#btn_savetofile'); @@ -565,6 +581,7 @@ $(document).ready(function(){ aidg_close = $("#btn_aidgpopupclose"); saveaspopup = $("#saveascontainer"); saveasinput = $("#savename"); + topic = $("#topic"); saveas_accept = $("#btn_saveasaccept"); saveas_close = $("#btn_saveasclose"); saveasoverwrite = $("#saveasoverwrite"); @@ -575,6 +592,9 @@ $(document).ready(function(){ nspopup = $("#newgamecontainer"); ns_accept = $("#btn_nsaccept"); ns_close = $("#btn_nsclose"); + rspopup = $("#rndgamecontainer"); + rs_accept = $("#btn_rsaccept"); + rs_close = $("#btn_rsclose"); seqselmenu = $("#seqselmenu"); seqselcontents = $("#seqselcontents"); @@ -902,6 +922,19 @@ $(document).ready(function(){ hideNewStoryPopup(); }); + button_rndgame.on("click", function(ev) { + showRandomStoryPopup(); + }); + + rs_accept.on("click", function(ev) { + socket.send({'cmd': 'rndgame', 'data': topic.val()}); + hideRandomStoryPopup(); + }); + + rs_close.on("click", function(ev) { + hideRandomStoryPopup(); + }); + anote_slider.on("input", function () { socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); }); diff --git a/static/application.js.bak b/static/application.js.bak new file mode 100644 index 00000000..71c066d4 --- /dev/null +++ b/static/application.js.bak @@ -0,0 +1,981 @@ +//=================================================================// +// VARIABLES +//=================================================================// + +// Socket IO Object +var socket; + +// UI references for jQuery +var connect_status; +var button_newgame; +var button_rndgame; +var button_save; +var button_saveas; +var button_savetofile; +var button_load; +var button_import; +var button_importwi; +var button_impaidg; +var button_settings; +var button_format; +var button_send; +var button_actedit; +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; +var anote_slider; +var popup; +var popup_title; +var popup_content; +var popup_accept; +var popup_close; +var aidgpopup; +var aidgpromptnum; +var aidg_accept; +var aidg_close; +var saveaspopup; +var saveasinput; +var saveas_accept; +var saveas_close; +var saveasoverwrite; +var loadpopup; +var loadcontent; +var load_accept; +var load_close; +var nspopup; +var ns_accept; +var ns_close; +var rspopup; +var rs_accept; +var rs_close; +var seqselmenu; +var seqselcontents; + +// Key states +var shift_down = false; +var do_clear_ent = false; + +// Display vars +var allowtoggle = false; +var formatcount = 0; + +//=================================================================// +// METHODS +//=================================================================// + +function addSetting(ob) { + // Add setting block to Settings Menu + if(ob.uitype == "slider"){ + settings_menu.append("
\ +
\ +
\ + "+ob.label+" ?"+ob.tooltip+"\ +
\ +
\ + "+ob.default+"\ +
\ +
\ +
\ + \ +
\ +
\ +
\ + "+ob.min+"\ +
\ +
\ + "+ob.max+"\ +
\ +
\ +
"); + // Set references to HTML objects + var refin = $("#"+ob.id); + var reflb = $("#"+ob.id+"cur"); + window["setting_"+ob.id] = refin; // Is this still needed? + window["label_"+ob.id] = reflb; // Is this still needed? + // Add event function to input + refin.on("input", function () { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).val()}); + }); + } else if(ob.uitype == "toggle"){ + settings_menu.append("
\ + \ + "+ob.label+" \ + ?"+ob.tooltip+"\ +
"); + // Tell Bootstrap-Toggle to render the new checkbox + $("input[type=checkbox]").bootstrapToggle(); + $("#"+ob.id).on("change", function () { + if(allowtoggle) { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); + } + }); + } +} + +function addFormat(ob) { + // Check if we need to make a new column for this button + if(formatcount == 0) { + format_menu.append("
"); + } + // Get reference to the last child column + var ref = $("#formatmenu > div").last(); + // Add format block to Format Menu + ref.append("
\ + \ + "+ob.label+" \ + ?"+ob.tooltip+"\ +
"); + // Tell Bootstrap-Toggle to render the new checkbox + $("input[type=checkbox]").bootstrapToggle(); + // Add event to input + $("#"+ob.id).on("change", function () { + if(allowtoggle) { + socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); + } + }); + // Increment display variable + formatcount++; + if(formatcount == 2) { + formatcount = 0; + } +} + +function addImportLine(ob) { + popup_content.append("
\ +
"+ob.title+"
\ +
"+ob.acts+"
\ +
"+ob.descr+"
\ +
"); + $("#import"+ob.num).on("click", function () { + socket.send({'cmd': 'importselect', 'data': $(this).attr('id')}); + highlightImportLine($(this)); + }); +} + +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"); + enableButtons([popup_accept]); +} + +function enableButtons(refs) { + for(i=0; i"); +} + +function hideWaitAnimation() { + $('#waitanim').remove(); +} + +function hide(refs) { + for(i=0; i *', function() { + editModeSelect($(this).attr("n")); + }); + disableSendBtn(); + hide([button_actback, button_actmem, button_actretry, button_actwi]); + show([button_delete]); +} + +function exitEditMode() { + // Remove class to each story chunk + hideMessage(); + button_actedit.html("Edit"); + game_text.children('chunk').removeClass("chunkhov"); + game_text.off('click', '> *'); + enableSendBtn(); + show([button_actback, button_actmem, button_actretry, button_actwi]); + hide([button_delete]); + input_text.val(""); +} + +function editModeSelect(n) { + socket.send({'cmd': 'editline', 'data': 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, button_actwi]); + // Display Author's Note field + anote_menu.slideDown("fast"); +} + +function exitMemoryMode() { + hideMessage(); + button_actmem.html("Memory"); + 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; i\ +
"+ar[i].name+"
\ +
"+ar[i].actions+"
\ + "); + $("#load"+i).on("click", function () { + enableButtons([load_accept]); + socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")}); + highlightLoadLine($(this)); + }); + } +} + +function highlightLoadLine(ref) { + $("#loadlistcontent > div").removeClass("popuplistselected"); + ref.addClass("popuplistselected"); +} + +function showNewStoryPopup() { + nspopup.removeClass("hidden"); + nspopup.addClass("flex"); +} + +function hideNewStoryPopup() { + nspopup.removeClass("flex"); + nspopup.addClass("hidden"); +} + +function showRandomStoryPopup() { + rspopup.removeClass("hidden"); + rspopup.addClass("flex"); +} + +function hideRandomStoryPopup() { + rspopup.removeClass("flex"); + rspopup.addClass("hidden"); +} + +function setStartState() { + enableSendBtn(); + 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(); + hideWaitAnimation(); + button_actedit.html("Edit"); + button_actmem.html("Memory"); + button_actwi.html("W Info"); + hideAidgPopup(); + hideSaveAsPopup(); + hideLoadPopup(); + hideNewStoryPopup(); + hidegenseqs(); +} + +function parsegenseqs(seqs) { + seqselcontents.html(""); + var i; + for(i=0; i"+seqs[i].generated_text+""); + $("#seqsel"+i).on("click", function () { + socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")}); + }); + } + $('#seqselmenu').slideDown("slow"); +} + +function hidegenseqs() { + $('#seqselmenu').slideUp("slow", function() { + seqselcontents.html(""); + }); +} + +//=================================================================// +// READY/RUNTIME +//=================================================================// + +$(document).ready(function(){ + + // Bind UI references + connect_status = $('#connectstatus'); + button_newgame = $('#btn_newgame'); + button_rndgame = $('#btn_rndgame'); + button_save = $('#btn_save'); + button_saveas = $('#btn_saveas'); + button_savetofile = $('#btn_savetofile'); + button_load = $('#btn_load'); + button_loadfrfile = $('#btn_loadfromfile'); + button_import = $("#btn_import"); + button_importwi = $("#btn_importwi"); + button_impaidg = $("#btn_impaidg"); + button_settings = $('#btn_settings'); + button_format = $('#btn_format'); + button_send = $('#btnsend'); + button_actedit = $('#btn_actedit'); + button_actmem = $('#btn_actmem'); + button_actback = $('#btn_actundo'); + button_actretry = $('#btn_actretry'); + button_delete = $('#btn_delete'); + button_actwi = $('#btn_actwi'); + game_text = $('#gametext'); + input_text = $('#input_text'); + message_text = $('#messagefield'); + settings_menu = $("#settingsmenu"); + format_menu = $('#formatmenu'); + anote_menu = $('#anoterowcontainer'); + wi_menu = $('#wimenu'); + anote_input = $('#anoteinput'); + anote_labelcur = $('#anotecur'); + anote_slider = $('#anotedepth'); + popup = $("#popupcontainer"); + popup_title = $("#popuptitletext"); + popup_content = $("#popupcontent"); + popup_accept = $("#btn_popupaccept"); + popup_close = $("#btn_popupclose"); + aidgpopup = $("#aidgpopupcontainer"); + aidgpromptnum = $("#aidgpromptnum"); + aidg_accept = $("#btn_aidgpopupaccept"); + aidg_close = $("#btn_aidgpopupclose"); + saveaspopup = $("#saveascontainer"); + saveasinput = $("#savename"); + saveas_accept = $("#btn_saveasaccept"); + saveas_close = $("#btn_saveasclose"); + saveasoverwrite = $("#saveasoverwrite"); + loadpopup = $("#loadcontainer"); + loadcontent = $("#loadlistcontent"); + load_accept = $("#btn_loadaccept"); + load_close = $("#btn_loadclose"); + nspopup = $("#newgamecontainer"); + ns_accept = $("#btn_nsaccept"); + ns_close = $("#btn_nsclose"); + rspopup = $("#rndgamecontainer"); + rs_accept = $("#btn_rsaccept"); + rs_close = $("#btn_rsclose"); + seqselmenu = $("#seqselmenu"); + seqselcontents = $("#seqselcontents"); + + // Connect to SocketIO server + loc = window.document.location; + socket = io.connect(loc.href); + + socket.on('from_server', function(msg) { + if(msg.cmd == "connected") { + // Connected to Server Actions + connect_status.html("Connected to KoboldAI Process!"); + connect_status.removeClass("color_orange"); + connect_status.addClass("color_green"); + // 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); + // Scroll to bottom of text + setTimeout(function () { + $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); + }, 5); + } else if(msg.cmd == "setgamestate") { + // Enable or Disable buttons + if(msg.data == "ready") { + enableSendBtn(); + enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + hideWaitAnimation(); + } else if(msg.data == "wait") { + disableSendBtn(); + disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + showWaitAnimation(); + } else if(msg.data == "start") { + setStartState(); + } + } else if(msg.cmd == "editmode") { + // Enable or Disable edit mode + if(msg.data == "true") { + enterEditMode(); + } else { + exitEditMode(); + } + } else if(msg.cmd == "setinputtext") { + // Set input box text for edit mode + input_text.val(msg.data); + } else if(msg.cmd == "enablesubmit") { + // Enables the submit button + enableSendBtn(); + } else if(msg.cmd == "memmode") { + // Enable or Disable memory edit mode + if(msg.data == "true") { + enterMemoryMode(); + } else { + exitMemoryMode(); + } + } else if(msg.cmd == "errmsg") { + // Send error message + errMessage(msg.data); + } else if(msg.cmd == "texteffect") { + // Apply color highlight to line of text + newTextHighlight($("#n"+msg.data)) + } else if(msg.cmd == "updatetemp") { + // Send current temp value to input + $("#settemp").val(parseFloat(msg.data)); + $("#settempcur").html(msg.data); + } else if(msg.cmd == "updatetopp") { + // Send current top p value to input + $("#settopp").val(parseFloat(msg.data)); + $("#settoppcur").html(msg.data); + } else if(msg.cmd == "updatereppen") { + // Send current rep pen value to input + $("#setreppen").val(parseFloat(msg.data)); + $("#setreppencur").html(msg.data); + } else if(msg.cmd == "updateoutlen") { + // Send current output amt value to input + $("#setoutput").val(parseInt(msg.data)); + $("#setoutputcur").html(msg.data); + } else if(msg.cmd == "updatetknmax") { + // Send current max tokens value to input + $("#settknmax").val(parseInt(msg.data)); + $("#settknmaxcur").html(msg.data); + } else if(msg.cmd == "updateikgen") { + // Send current max tokens value to input + $("#setikgen").val(parseInt(msg.data)); + $("#setikgencur").html(msg.data); + } else if(msg.cmd == "setlabeltemp") { + // Update setting label with value from server + $("#settempcur").html(msg.data); + } else if(msg.cmd == "setlabeltopp") { + // Update setting label with value from server + $("#settoppcur").html(msg.data); + } else if(msg.cmd == "setlabelreppen") { + // Update setting label with value from server + $("#setreppencur").html(msg.data); + } else if(msg.cmd == "setlabeloutput") { + // Update setting label with value from server + $("#setoutputcur").html(msg.data); + } else if(msg.cmd == "setlabeltknmax") { + // Update setting label with value from server + $("#settknmaxcur").html(msg.data); + } else if(msg.cmd == "setlabelikgen") { + // Update setting label with value from server + $("#setikgencur").html(msg.data); + } else if(msg.cmd == "updateanotedepth") { + // Send current Author's Note depth value to input + anote_slider.val(parseInt(msg.data)); + anote_labelcur.html(msg.data); + } else if(msg.cmd == "setlabelanotedepth") { + // Update setting label with value from server + anote_labelcur.html(msg.data); + } else if(msg.cmd == "getanote") { + // Request contents of Author's Note field + var txt = anote_input.val(); + socket.send({'cmd': 'anote', 'data': txt}); + } else if(msg.cmd == "setanote") { + // Set contents of Author's Note field + anote_input.val(msg.data); + } else if(msg.cmd == "addsetting") { + // Add setting controls + addSetting(msg.data); + } else if(msg.cmd == "addformat") { + // Add setting controls + addFormat(msg.data); + } else if(msg.cmd == "updatefrmttriminc") { + // Update toggle state + $("#frmttriminc").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtrmblln") { + // Update toggle state + $("#frmtrmblln").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtrmspch") { + // Update toggle state + $("#frmtrmspch").prop('checked', msg.data).change(); + } else if(msg.cmd == "updatefrmtadsnsp") { + // Update toggle state + $("#frmtadsnsp").prop('checked', msg.data).change(); + } else if(msg.cmd == "allowtoggle") { + // Allow toggle change states to propagate + allowtoggle = msg.data; + } else if(msg.cmd == "popupshow") { + // Show/Hide Popup + popupShow(msg.data); + } else if(msg.cmd == "addimportline") { + // Add import popup entry + addImportLine(msg.data); + } 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); + } else if(msg.cmd == "saveas") { + // Show Save As prompt + showSaveAsPopup(); + } else if(msg.cmd == "hidesaveas") { + // Hide Save As prompt + hideSaveAsPopup(); + } else if(msg.cmd == "buildload") { + // Send array of save files to load UI + buildLoadList(msg.data); + } else if(msg.cmd == "askforoverwrite") { + // Show overwrite warning + show([saveasoverwrite]); + } else if(msg.cmd == "genseqs") { + // Parse generator sequences to UI + parsegenseqs(msg.data); + } else if(msg.cmd == "hidegenseqs") { + // Collapse genseqs menu + hidegenseqs(); + } else if(msg.cmd == "setlabelnumseq") { + // Update setting label with value from server + $("#setnumseqcur").html(msg.data); + } else if(msg.cmd == "updatenumseq") { + // Send current max tokens value to input + $("#setnumseq").val(parseInt(msg.data)); + $("#setnumseqcur").html(msg.data); + } else if(msg.cmd == "setlabelwidepth") { + // Update setting label with value from server + $("#setwidepthcur").html(msg.data); + } else if(msg.cmd == "updatewidepth") { + // Send current max tokens value to input + $("#setwidepth").val(parseInt(msg.data)); + $("#setwidepthcur").html(msg.data); + } else if(msg.cmd == "updateuseprompt") { + // Update toggle state + $("#setuseprompt").prop('checked', msg.data).change(); + } + }); + + socket.on('disconnect', function() { + connect_status.html("Lost connection..."); + connect_status.removeClass("color_green"); + connect_status.addClass("color_orange"); + }); + + // Bind actions to UI buttons + button_send.on("click", function(ev) { + dosubmit(); + }); + + button_actretry.on("click", function(ev) { + socket.send({'cmd': 'retry', 'data': ''}); + hidegenseqs(); + }); + + button_actback.on("click", function(ev) { + socket.send({'cmd': 'back', 'data': ''}); + hidegenseqs(); + }); + + button_actedit.on("click", function(ev) { + socket.send({'cmd': 'edit', 'data': ''}); + }); + + button_delete.on("click", function(ev) { + socket.send({'cmd': 'delete', 'data': ''}); + }); + + button_actmem.on("click", function(ev) { + socket.send({'cmd': 'memory', 'data': ''}); + }); + + button_savetofile.on("click", function(ev) { + socket.send({'cmd': 'savetofile', 'data': ''}); + }); + + button_loadfrfile.on("click", function(ev) { + socket.send({'cmd': 'loadfromfile', 'data': ''}); + }); + + button_import.on("click", function(ev) { + socket.send({'cmd': 'import', 'data': ''}); + }); + + button_importwi.on("click", function(ev) { + socket.send({'cmd': 'importwi', 'data': ''}); + }); + + button_settings.on("click", function(ev) { + $('#settingsmenu').slideToggle("slow"); + }); + + button_format.on("click", function(ev) { + $('#formatmenu').slideToggle("slow"); + }); + + popup_close.on("click", function(ev) { + socket.send({'cmd': 'importcancel', 'data': ''}); + }); + + popup_accept.on("click", function(ev) { + socket.send({'cmd': 'importaccept', 'data': ''}); + }); + + button_actwi.on("click", function(ev) { + socket.send({'cmd': 'wi', 'data': ''}); + }); + + button_impaidg.on("click", function(ev) { + showAidgPopup(); + }); + + aidg_close.on("click", function(ev) { + hideAidgPopup(); + }); + + aidg_accept.on("click", function(ev) { + sendAidgImportRequest(); + }); + + button_save.on("click", function(ev) { + socket.send({'cmd': 'saverequest', 'data': ''}); + }); + + button_saveas.on("click", function(ev) { + showSaveAsPopup(); + }); + + saveas_close.on("click", function(ev) { + hideSaveAsPopup(); + socket.send({'cmd': 'clearoverwrite', 'data': ''}); + }); + + saveas_accept.on("click", function(ev) { + sendSaveAsRequest(); + }); + + button_load.on("click", function(ev) { + socket.send({'cmd': 'loadlistrequest', 'data': ''}); + }); + + load_close.on("click", function(ev) { + hideLoadPopup(); + }); + + load_accept.on("click", function(ev) { + socket.send({'cmd': 'loadrequest', 'data': ''}); + hideLoadPopup(); + }); + + button_newgame.on("click", function(ev) { + showNewStoryPopup(); + }); + + ns_accept.on("click", function(ev) { + socket.send({'cmd': 'newgame', 'data': ''}); + hideNewStoryPopup(); + }); + + ns_close.on("click", function(ev) { + hideNewStoryPopup(); + }); + + button_rndgame.on("click", function(ev) { + showRandomStoryPopup(); + }); + + rs_accept.on("click", function(ev) { + socket.send({'cmd': 'rndgame', 'data': ''}); + hideRandomStoryPopup(); + }); + + rs_close.on("click", function(ev) { + hideRandomStoryPopup(); + }); + + anote_slider.on("input", function () { + socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); + }); + + saveasinput.on("input", function () { + if(saveasinput.val() == "") { + disableButtons([saveas_accept]); + } else { + enableButtons([saveas_accept]); + } + hide([saveasoverwrite]); + }); + + // Bind Enter button to submit + input_text.keydown(function (ev) { + if (ev.which == 13 && !shift_down) { + do_clear_ent = true; + dosubmit(); + } else if(ev.which == 16) { + shift_down = true; + } + }); + + // Enter to submit, but not if holding shift + input_text.keyup(function (ev) { + if (ev.which == 13 && do_clear_ent) { + input_text.val(""); + do_clear_ent = false; + } else if(ev.which == 16) { + shift_down = false; + } + }); + + aidgpromptnum.keydown(function (ev) { + if (ev.which == 13) { + sendAidgImportRequest(); + } + }); + + saveasinput.keydown(function (ev) { + if (ev.which == 13 && saveasinput.val() != "") { + sendSaveAsRequest(); + } + }); +}); + diff --git a/static/custom.css b/static/custom.css index 0fa720ca..f27bce24 100644 --- a/static/custom.css +++ b/static/custom.css @@ -271,6 +271,12 @@ chunk { margin-top: 200px; } +#rspopup { + width: 800px; + background-color: #262626; + margin-top: 200px; +} + /*================= Classes =================*/ .aidgpopupcontent { diff --git a/templates/index.html b/templates/index.html index 6efac6c5..a334f905 100644 --- a/templates/index.html +++ b/templates/index.html @@ -27,8 +27,12 @@ + - \ No newline at end of file + From 00414d26e28e48ff9dd50f1015a0e18bb69f801a Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 13:18:01 +0200 Subject: [PATCH 010/117] Integrated VE_FORBRYDERNE's Adventure Mode + Cleanup Adventure Mode allows you to play this like AID, perfect for Choose your own Adventure models --- aiserver.py | 48 +- gensettings.py | 22 + readme.txt | 9 +- static/application - Copy.js | 981 ----------------------------------- static/application.js | 66 ++- static/application.js.bak | 981 ----------------------------------- static/custom.css | 31 +- templates/index.html | 3 + utils.py | 7 +- 9 files changed, 171 insertions(+), 1977 deletions(-) delete mode 100644 static/application - Copy.js delete mode 100644 static/application.js.bak diff --git a/aiserver.py b/aiserver.py index ba583480..cb282e5b 100644 --- a/aiserver.py +++ b/aiserver.py @@ -6,6 +6,7 @@ # External packages from os import path, getcwd +import re import tkinter as tk from tkinter import messagebox import json @@ -96,6 +97,10 @@ class vars: saveow = False # Whether or not overwrite confirm has been displayed genseqs = [] # Temporary storage for generated sequences useprompt = True # Whether to send the full prompt with every submit action + acregex_ai = re.compile(r'\n* *>(.|\n)*') # Pattern for matching adventure actions from the AI so we can remove them + acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses) + actionmode = 1 + adventure = False #==================================================================# # Function to get model selection at startup @@ -399,7 +404,7 @@ def get_message(msg): # Submit action if(msg['cmd'] == 'submit'): if(vars.mode == "play"): - actionsubmit(msg['data']) + actionsubmit(msg['data'], actionmode=msg['actionmode']) elif(vars.mode == "edit"): editsubmit(msg['data']) elif(vars.mode == "memory"): @@ -531,6 +536,10 @@ def get_message(msg): elif(msg['cmd'] == 'setuseprompt'): vars.useprompt = msg['data'] settingschanged() + elif(msg['cmd'] == 'setadventure'): + vars.adventure = msg['data'] + settingschanged() + refresh_story() elif(msg['cmd'] == 'importwi'): wiimportrequest() @@ -543,7 +552,7 @@ def setStartState(): txt = txt + "Please load a game or enter a prompt below to begin!" else: txt = txt + "Please load or import a story to read. There is no AI in this mode." - emit('from_server', {'cmd': 'updatescreen', 'data': txt}) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': txt}) emit('from_server', {'cmd': 'setgamestate', 'data': 'start'}) #==================================================================# @@ -583,6 +592,7 @@ def savesettings(): js["numseqs"] = vars.numseqs js["widepth"] = vars.widepth js["useprompt"] = vars.useprompt + js["adventure"] = vars.adventure # Write it file = open("client.settings", "w") @@ -625,6 +635,8 @@ def loadsettings(): vars.widepth = js["widepth"] if("useprompt" in js): vars.useprompt = js["useprompt"] + if("adventure" in js): + vars.adventure = js["adventure"] file.close() @@ -639,11 +651,19 @@ def settingschanged(): #==================================================================# # Take input text from SocketIO and decide what to do with it #==================================================================# -def actionsubmit(data): +def actionsubmit(data, actionmode=0): # Ignore new submissions if the AI is currently busy if(vars.aibusy): return set_aibusy(1) + + vars.actionmode = actionmode + + # "Action" mode + if(actionmode == 1): + data = data.strip().lstrip('>') + data = re.sub(r'\n+', ' ', data) + data = f"\n\n> {data}\n" # If we're not continuing, store a copy of the raw input if(data != ""): @@ -656,7 +676,7 @@ def actionsubmit(data): vars.prompt = data if(not vars.noai): # Clear the startup text from game screen - emit('from_server', {'cmd': 'updatescreen', 'data': 'Please wait, generating story...'}) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': 'Please wait, generating story...'}) calcsubmit(data) # Run the first action through the generator else: refresh_story() @@ -665,7 +685,8 @@ def actionsubmit(data): # Dont append submission if it's a blank/continue action if(data != ""): # Apply input formatting & scripts before sending to tokenizer - data = applyinputformatting(data) + if(vars.actionmode == 0): + data = applyinputformatting(data) # Store the result in the Action log vars.actions.append(data) @@ -1076,6 +1097,10 @@ def applyinputformatting(txt): def applyoutputformatting(txt): # Use standard quotes and apostrophes txt = utils.fixquotes(txt) + + # Adventure mode clipping of all characters after '>' + if(vars.adventure): + txt = vars.acregex_ai.sub('', txt) # Trim incomplete sentences if(vars.formatoptns["frmttriminc"]): @@ -1085,7 +1110,7 @@ def applyoutputformatting(txt): txt = utils.replaceblanklines(txt) # Remove special characters if(vars.formatoptns["frmtrmspch"]): - txt = utils.removespecialchars(txt) + txt = utils.removespecialchars(txt, vars) return txt @@ -1095,8 +1120,10 @@ def applyoutputformatting(txt): def refresh_story(): text_parts = ['', html.escape(vars.prompt), ''] for idx, item in enumerate(vars.actions, start=1): - text_parts.extend(('', html.escape(item), '')) - emit('from_server', {'cmd': 'updatescreen', 'data': formatforhtml(''.join(text_parts))}) + if vars.adventure: # Add special formatting to adventure actions + item = vars.acregex_ui.sub('\\1', html.escape(item)) + text_parts.extend(('', item, '')) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': formatforhtml(''.join(text_parts))}) #==================================================================# # Sends the current generator settings to the Game Menu @@ -1120,6 +1147,7 @@ def refresh_settings(): emit('from_server', {'cmd': 'updateanotedepth', 'data': vars.andepth}) emit('from_server', {'cmd': 'updatewidepth', 'data': vars.widepth}) emit('from_server', {'cmd': 'updateuseprompt', 'data': vars.useprompt}) + emit('from_server', {'cmd': 'updateadventure', 'data': vars.adventure}) emit('from_server', {'cmd': 'updatefrmttriminc', 'data': vars.formatoptns["frmttriminc"]}) emit('from_server', {'cmd': 'updatefrmtrmblln', 'data': vars.formatoptns["frmtrmblln"]}) @@ -1820,5 +1848,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, host='0.0.0.0', port=5000) - #socketio.run(app) + #socketio.run(app, host='0.0.0.0', port=5000) + socketio.run(app) diff --git a/gensettings.py b/gensettings.py index eb914139..da3aaa76 100644 --- a/gensettings.py +++ b/gensettings.py @@ -85,6 +85,17 @@ gensettingstf = [{ "step": 1, "default": 1, "tooltip": "Whether the prompt should be sent in the context of every action." + }, + { + "uitype": "toggle", + "unit": "bool", + "label": "Adventure Mode", + "id": "setadventure", + "min": 0, + "max": 1, + "step": 1, + "default": 0, + "tooltip": "Turn this on if you are playing a Choose your Adventure model." }] gensettingsik =[{ @@ -141,6 +152,17 @@ gensettingsik =[{ "step": 1, "default": 1, "tooltip": "Whether the prompt should be sent in the context of every action." + }, + { + "uitype": "toggle", + "unit": "bool", + "label": "Adventure Mode", + "id": "setadventure", + "min": 0, + "max": 1, + "step": 1, + "default": 0, + "tooltip": "Turn this on if you are playing a Choose your Adventure model." }] formatcontrols = [{ diff --git a/readme.txt b/readme.txt index 9ab782bb..7b4d93c4 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,13 @@ This branch will eventually be used for a community edition of KoboldAI, uniting the different community made editions. -For now it is a WIP branch with Random Story Generation and better default settings. +For now it is a WIP branch as I integrate the community features. +Once done i will be replacing this with a better page describing KoboldAI. --- Looking for ColabKobold? Check the different branches on my account. + +--- +Contains work from : +- All the original KoboldAI creators +- Henky!! (Random Story Generator / Setting Optimizations) +- VE_FORBRYDERNE (Adventure Mode) \ No newline at end of file diff --git a/static/application - Copy.js b/static/application - Copy.js deleted file mode 100644 index 71c066d4..00000000 --- a/static/application - Copy.js +++ /dev/null @@ -1,981 +0,0 @@ -//=================================================================// -// VARIABLES -//=================================================================// - -// Socket IO Object -var socket; - -// UI references for jQuery -var connect_status; -var button_newgame; -var button_rndgame; -var button_save; -var button_saveas; -var button_savetofile; -var button_load; -var button_import; -var button_importwi; -var button_impaidg; -var button_settings; -var button_format; -var button_send; -var button_actedit; -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; -var anote_slider; -var popup; -var popup_title; -var popup_content; -var popup_accept; -var popup_close; -var aidgpopup; -var aidgpromptnum; -var aidg_accept; -var aidg_close; -var saveaspopup; -var saveasinput; -var saveas_accept; -var saveas_close; -var saveasoverwrite; -var loadpopup; -var loadcontent; -var load_accept; -var load_close; -var nspopup; -var ns_accept; -var ns_close; -var rspopup; -var rs_accept; -var rs_close; -var seqselmenu; -var seqselcontents; - -// Key states -var shift_down = false; -var do_clear_ent = false; - -// Display vars -var allowtoggle = false; -var formatcount = 0; - -//=================================================================// -// METHODS -//=================================================================// - -function addSetting(ob) { - // Add setting block to Settings Menu - if(ob.uitype == "slider"){ - settings_menu.append("
\ -
\ -
\ - "+ob.label+" ?"+ob.tooltip+"\ -
\ -
\ - "+ob.default+"\ -
\ -
\ -
\ - \ -
\ -
\ -
\ - "+ob.min+"\ -
\ -
\ - "+ob.max+"\ -
\ -
\ -
"); - // Set references to HTML objects - var refin = $("#"+ob.id); - var reflb = $("#"+ob.id+"cur"); - window["setting_"+ob.id] = refin; // Is this still needed? - window["label_"+ob.id] = reflb; // Is this still needed? - // Add event function to input - refin.on("input", function () { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).val()}); - }); - } else if(ob.uitype == "toggle"){ - settings_menu.append("
\ - \ - "+ob.label+" \ - ?"+ob.tooltip+"\ -
"); - // Tell Bootstrap-Toggle to render the new checkbox - $("input[type=checkbox]").bootstrapToggle(); - $("#"+ob.id).on("change", function () { - if(allowtoggle) { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); - } - }); - } -} - -function addFormat(ob) { - // Check if we need to make a new column for this button - if(formatcount == 0) { - format_menu.append("
"); - } - // Get reference to the last child column - var ref = $("#formatmenu > div").last(); - // Add format block to Format Menu - ref.append("
\ - \ - "+ob.label+" \ - ?"+ob.tooltip+"\ -
"); - // Tell Bootstrap-Toggle to render the new checkbox - $("input[type=checkbox]").bootstrapToggle(); - // Add event to input - $("#"+ob.id).on("change", function () { - if(allowtoggle) { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); - } - }); - // Increment display variable - formatcount++; - if(formatcount == 2) { - formatcount = 0; - } -} - -function addImportLine(ob) { - popup_content.append("
\ -
"+ob.title+"
\ -
"+ob.acts+"
\ -
"+ob.descr+"
\ -
"); - $("#import"+ob.num).on("click", function () { - socket.send({'cmd': 'importselect', 'data': $(this).attr('id')}); - highlightImportLine($(this)); - }); -} - -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"); - enableButtons([popup_accept]); -} - -function enableButtons(refs) { - for(i=0; i"); -} - -function hideWaitAnimation() { - $('#waitanim').remove(); -} - -function hide(refs) { - for(i=0; i *', function() { - editModeSelect($(this).attr("n")); - }); - disableSendBtn(); - hide([button_actback, button_actmem, button_actretry, button_actwi]); - show([button_delete]); -} - -function exitEditMode() { - // Remove class to each story chunk - hideMessage(); - button_actedit.html("Edit"); - game_text.children('chunk').removeClass("chunkhov"); - game_text.off('click', '> *'); - enableSendBtn(); - show([button_actback, button_actmem, button_actretry, button_actwi]); - hide([button_delete]); - input_text.val(""); -} - -function editModeSelect(n) { - socket.send({'cmd': 'editline', 'data': 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, button_actwi]); - // Display Author's Note field - anote_menu.slideDown("fast"); -} - -function exitMemoryMode() { - hideMessage(); - button_actmem.html("Memory"); - 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; i\ -
"+ar[i].name+"
\ -
"+ar[i].actions+"
\ - "); - $("#load"+i).on("click", function () { - enableButtons([load_accept]); - socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")}); - highlightLoadLine($(this)); - }); - } -} - -function highlightLoadLine(ref) { - $("#loadlistcontent > div").removeClass("popuplistselected"); - ref.addClass("popuplistselected"); -} - -function showNewStoryPopup() { - nspopup.removeClass("hidden"); - nspopup.addClass("flex"); -} - -function hideNewStoryPopup() { - nspopup.removeClass("flex"); - nspopup.addClass("hidden"); -} - -function showRandomStoryPopup() { - rspopup.removeClass("hidden"); - rspopup.addClass("flex"); -} - -function hideRandomStoryPopup() { - rspopup.removeClass("flex"); - rspopup.addClass("hidden"); -} - -function setStartState() { - enableSendBtn(); - 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(); - hideWaitAnimation(); - button_actedit.html("Edit"); - button_actmem.html("Memory"); - button_actwi.html("W Info"); - hideAidgPopup(); - hideSaveAsPopup(); - hideLoadPopup(); - hideNewStoryPopup(); - hidegenseqs(); -} - -function parsegenseqs(seqs) { - seqselcontents.html(""); - var i; - for(i=0; i"+seqs[i].generated_text+""); - $("#seqsel"+i).on("click", function () { - socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")}); - }); - } - $('#seqselmenu').slideDown("slow"); -} - -function hidegenseqs() { - $('#seqselmenu').slideUp("slow", function() { - seqselcontents.html(""); - }); -} - -//=================================================================// -// READY/RUNTIME -//=================================================================// - -$(document).ready(function(){ - - // Bind UI references - connect_status = $('#connectstatus'); - button_newgame = $('#btn_newgame'); - button_rndgame = $('#btn_rndgame'); - button_save = $('#btn_save'); - button_saveas = $('#btn_saveas'); - button_savetofile = $('#btn_savetofile'); - button_load = $('#btn_load'); - button_loadfrfile = $('#btn_loadfromfile'); - button_import = $("#btn_import"); - button_importwi = $("#btn_importwi"); - button_impaidg = $("#btn_impaidg"); - button_settings = $('#btn_settings'); - button_format = $('#btn_format'); - button_send = $('#btnsend'); - button_actedit = $('#btn_actedit'); - button_actmem = $('#btn_actmem'); - button_actback = $('#btn_actundo'); - button_actretry = $('#btn_actretry'); - button_delete = $('#btn_delete'); - button_actwi = $('#btn_actwi'); - game_text = $('#gametext'); - input_text = $('#input_text'); - message_text = $('#messagefield'); - settings_menu = $("#settingsmenu"); - format_menu = $('#formatmenu'); - anote_menu = $('#anoterowcontainer'); - wi_menu = $('#wimenu'); - anote_input = $('#anoteinput'); - anote_labelcur = $('#anotecur'); - anote_slider = $('#anotedepth'); - popup = $("#popupcontainer"); - popup_title = $("#popuptitletext"); - popup_content = $("#popupcontent"); - popup_accept = $("#btn_popupaccept"); - popup_close = $("#btn_popupclose"); - aidgpopup = $("#aidgpopupcontainer"); - aidgpromptnum = $("#aidgpromptnum"); - aidg_accept = $("#btn_aidgpopupaccept"); - aidg_close = $("#btn_aidgpopupclose"); - saveaspopup = $("#saveascontainer"); - saveasinput = $("#savename"); - saveas_accept = $("#btn_saveasaccept"); - saveas_close = $("#btn_saveasclose"); - saveasoverwrite = $("#saveasoverwrite"); - loadpopup = $("#loadcontainer"); - loadcontent = $("#loadlistcontent"); - load_accept = $("#btn_loadaccept"); - load_close = $("#btn_loadclose"); - nspopup = $("#newgamecontainer"); - ns_accept = $("#btn_nsaccept"); - ns_close = $("#btn_nsclose"); - rspopup = $("#rndgamecontainer"); - rs_accept = $("#btn_rsaccept"); - rs_close = $("#btn_rsclose"); - seqselmenu = $("#seqselmenu"); - seqselcontents = $("#seqselcontents"); - - // Connect to SocketIO server - loc = window.document.location; - socket = io.connect(loc.href); - - socket.on('from_server', function(msg) { - if(msg.cmd == "connected") { - // Connected to Server Actions - connect_status.html("Connected to KoboldAI Process!"); - connect_status.removeClass("color_orange"); - connect_status.addClass("color_green"); - // 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); - // Scroll to bottom of text - setTimeout(function () { - $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); - }, 5); - } else if(msg.cmd == "setgamestate") { - // Enable or Disable buttons - if(msg.data == "ready") { - enableSendBtn(); - enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); - hideWaitAnimation(); - } else if(msg.data == "wait") { - disableSendBtn(); - disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); - showWaitAnimation(); - } else if(msg.data == "start") { - setStartState(); - } - } else if(msg.cmd == "editmode") { - // Enable or Disable edit mode - if(msg.data == "true") { - enterEditMode(); - } else { - exitEditMode(); - } - } else if(msg.cmd == "setinputtext") { - // Set input box text for edit mode - input_text.val(msg.data); - } else if(msg.cmd == "enablesubmit") { - // Enables the submit button - enableSendBtn(); - } else if(msg.cmd == "memmode") { - // Enable or Disable memory edit mode - if(msg.data == "true") { - enterMemoryMode(); - } else { - exitMemoryMode(); - } - } else if(msg.cmd == "errmsg") { - // Send error message - errMessage(msg.data); - } else if(msg.cmd == "texteffect") { - // Apply color highlight to line of text - newTextHighlight($("#n"+msg.data)) - } else if(msg.cmd == "updatetemp") { - // Send current temp value to input - $("#settemp").val(parseFloat(msg.data)); - $("#settempcur").html(msg.data); - } else if(msg.cmd == "updatetopp") { - // Send current top p value to input - $("#settopp").val(parseFloat(msg.data)); - $("#settoppcur").html(msg.data); - } else if(msg.cmd == "updatereppen") { - // Send current rep pen value to input - $("#setreppen").val(parseFloat(msg.data)); - $("#setreppencur").html(msg.data); - } else if(msg.cmd == "updateoutlen") { - // Send current output amt value to input - $("#setoutput").val(parseInt(msg.data)); - $("#setoutputcur").html(msg.data); - } else if(msg.cmd == "updatetknmax") { - // Send current max tokens value to input - $("#settknmax").val(parseInt(msg.data)); - $("#settknmaxcur").html(msg.data); - } else if(msg.cmd == "updateikgen") { - // Send current max tokens value to input - $("#setikgen").val(parseInt(msg.data)); - $("#setikgencur").html(msg.data); - } else if(msg.cmd == "setlabeltemp") { - // Update setting label with value from server - $("#settempcur").html(msg.data); - } else if(msg.cmd == "setlabeltopp") { - // Update setting label with value from server - $("#settoppcur").html(msg.data); - } else if(msg.cmd == "setlabelreppen") { - // Update setting label with value from server - $("#setreppencur").html(msg.data); - } else if(msg.cmd == "setlabeloutput") { - // Update setting label with value from server - $("#setoutputcur").html(msg.data); - } else if(msg.cmd == "setlabeltknmax") { - // Update setting label with value from server - $("#settknmaxcur").html(msg.data); - } else if(msg.cmd == "setlabelikgen") { - // Update setting label with value from server - $("#setikgencur").html(msg.data); - } else if(msg.cmd == "updateanotedepth") { - // Send current Author's Note depth value to input - anote_slider.val(parseInt(msg.data)); - anote_labelcur.html(msg.data); - } else if(msg.cmd == "setlabelanotedepth") { - // Update setting label with value from server - anote_labelcur.html(msg.data); - } else if(msg.cmd == "getanote") { - // Request contents of Author's Note field - var txt = anote_input.val(); - socket.send({'cmd': 'anote', 'data': txt}); - } else if(msg.cmd == "setanote") { - // Set contents of Author's Note field - anote_input.val(msg.data); - } else if(msg.cmd == "addsetting") { - // Add setting controls - addSetting(msg.data); - } else if(msg.cmd == "addformat") { - // Add setting controls - addFormat(msg.data); - } else if(msg.cmd == "updatefrmttriminc") { - // Update toggle state - $("#frmttriminc").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtrmblln") { - // Update toggle state - $("#frmtrmblln").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtrmspch") { - // Update toggle state - $("#frmtrmspch").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtadsnsp") { - // Update toggle state - $("#frmtadsnsp").prop('checked', msg.data).change(); - } else if(msg.cmd == "allowtoggle") { - // Allow toggle change states to propagate - allowtoggle = msg.data; - } else if(msg.cmd == "popupshow") { - // Show/Hide Popup - popupShow(msg.data); - } else if(msg.cmd == "addimportline") { - // Add import popup entry - addImportLine(msg.data); - } 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); - } else if(msg.cmd == "saveas") { - // Show Save As prompt - showSaveAsPopup(); - } else if(msg.cmd == "hidesaveas") { - // Hide Save As prompt - hideSaveAsPopup(); - } else if(msg.cmd == "buildload") { - // Send array of save files to load UI - buildLoadList(msg.data); - } else if(msg.cmd == "askforoverwrite") { - // Show overwrite warning - show([saveasoverwrite]); - } else if(msg.cmd == "genseqs") { - // Parse generator sequences to UI - parsegenseqs(msg.data); - } else if(msg.cmd == "hidegenseqs") { - // Collapse genseqs menu - hidegenseqs(); - } else if(msg.cmd == "setlabelnumseq") { - // Update setting label with value from server - $("#setnumseqcur").html(msg.data); - } else if(msg.cmd == "updatenumseq") { - // Send current max tokens value to input - $("#setnumseq").val(parseInt(msg.data)); - $("#setnumseqcur").html(msg.data); - } else if(msg.cmd == "setlabelwidepth") { - // Update setting label with value from server - $("#setwidepthcur").html(msg.data); - } else if(msg.cmd == "updatewidepth") { - // Send current max tokens value to input - $("#setwidepth").val(parseInt(msg.data)); - $("#setwidepthcur").html(msg.data); - } else if(msg.cmd == "updateuseprompt") { - // Update toggle state - $("#setuseprompt").prop('checked', msg.data).change(); - } - }); - - socket.on('disconnect', function() { - connect_status.html("Lost connection..."); - connect_status.removeClass("color_green"); - connect_status.addClass("color_orange"); - }); - - // Bind actions to UI buttons - button_send.on("click", function(ev) { - dosubmit(); - }); - - button_actretry.on("click", function(ev) { - socket.send({'cmd': 'retry', 'data': ''}); - hidegenseqs(); - }); - - button_actback.on("click", function(ev) { - socket.send({'cmd': 'back', 'data': ''}); - hidegenseqs(); - }); - - button_actedit.on("click", function(ev) { - socket.send({'cmd': 'edit', 'data': ''}); - }); - - button_delete.on("click", function(ev) { - socket.send({'cmd': 'delete', 'data': ''}); - }); - - button_actmem.on("click", function(ev) { - socket.send({'cmd': 'memory', 'data': ''}); - }); - - button_savetofile.on("click", function(ev) { - socket.send({'cmd': 'savetofile', 'data': ''}); - }); - - button_loadfrfile.on("click", function(ev) { - socket.send({'cmd': 'loadfromfile', 'data': ''}); - }); - - button_import.on("click", function(ev) { - socket.send({'cmd': 'import', 'data': ''}); - }); - - button_importwi.on("click", function(ev) { - socket.send({'cmd': 'importwi', 'data': ''}); - }); - - button_settings.on("click", function(ev) { - $('#settingsmenu').slideToggle("slow"); - }); - - button_format.on("click", function(ev) { - $('#formatmenu').slideToggle("slow"); - }); - - popup_close.on("click", function(ev) { - socket.send({'cmd': 'importcancel', 'data': ''}); - }); - - popup_accept.on("click", function(ev) { - socket.send({'cmd': 'importaccept', 'data': ''}); - }); - - button_actwi.on("click", function(ev) { - socket.send({'cmd': 'wi', 'data': ''}); - }); - - button_impaidg.on("click", function(ev) { - showAidgPopup(); - }); - - aidg_close.on("click", function(ev) { - hideAidgPopup(); - }); - - aidg_accept.on("click", function(ev) { - sendAidgImportRequest(); - }); - - button_save.on("click", function(ev) { - socket.send({'cmd': 'saverequest', 'data': ''}); - }); - - button_saveas.on("click", function(ev) { - showSaveAsPopup(); - }); - - saveas_close.on("click", function(ev) { - hideSaveAsPopup(); - socket.send({'cmd': 'clearoverwrite', 'data': ''}); - }); - - saveas_accept.on("click", function(ev) { - sendSaveAsRequest(); - }); - - button_load.on("click", function(ev) { - socket.send({'cmd': 'loadlistrequest', 'data': ''}); - }); - - load_close.on("click", function(ev) { - hideLoadPopup(); - }); - - load_accept.on("click", function(ev) { - socket.send({'cmd': 'loadrequest', 'data': ''}); - hideLoadPopup(); - }); - - button_newgame.on("click", function(ev) { - showNewStoryPopup(); - }); - - ns_accept.on("click", function(ev) { - socket.send({'cmd': 'newgame', 'data': ''}); - hideNewStoryPopup(); - }); - - ns_close.on("click", function(ev) { - hideNewStoryPopup(); - }); - - button_rndgame.on("click", function(ev) { - showRandomStoryPopup(); - }); - - rs_accept.on("click", function(ev) { - socket.send({'cmd': 'rndgame', 'data': ''}); - hideRandomStoryPopup(); - }); - - rs_close.on("click", function(ev) { - hideRandomStoryPopup(); - }); - - anote_slider.on("input", function () { - socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); - }); - - saveasinput.on("input", function () { - if(saveasinput.val() == "") { - disableButtons([saveas_accept]); - } else { - enableButtons([saveas_accept]); - } - hide([saveasoverwrite]); - }); - - // Bind Enter button to submit - input_text.keydown(function (ev) { - if (ev.which == 13 && !shift_down) { - do_clear_ent = true; - dosubmit(); - } else if(ev.which == 16) { - shift_down = true; - } - }); - - // Enter to submit, but not if holding shift - input_text.keyup(function (ev) { - if (ev.which == 13 && do_clear_ent) { - input_text.val(""); - do_clear_ent = false; - } else if(ev.which == 16) { - shift_down = false; - } - }); - - aidgpromptnum.keydown(function (ev) { - if (ev.which == 13) { - sendAidgImportRequest(); - } - }); - - saveasinput.keydown(function (ev) { - if (ev.which == 13 && saveasinput.val() != "") { - sendSaveAsRequest(); - } - }); -}); - diff --git a/static/application.js b/static/application.js index 5a4a0086..5a01f289 100644 --- a/static/application.js +++ b/static/application.js @@ -18,6 +18,8 @@ var button_importwi; var button_impaidg; var button_settings; var button_format; +var button_mode; +var button_mode_label; var button_send; var button_actedit; var button_actmem; @@ -63,6 +65,9 @@ var rs_close; var seqselmenu; var seqselcontents; +var memorymode = false; +var gamestarted = false; + // Key states var shift_down = false; var do_clear_ent = false; @@ -71,6 +76,10 @@ var do_clear_ent = false; var allowtoggle = false; var formatcount = 0; +// Adventure +var action_mode = 0; // 0: story, 1: action +var adventure = false; + //=================================================================// // METHODS //=================================================================// @@ -120,6 +129,9 @@ function addSetting(ob) { if(allowtoggle) { socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); } + if(ob.id == "setadventure"){ + setadventure($(this).prop('checked')); + } }); } } @@ -345,6 +357,8 @@ function editModeSelect(n) { } function enterMemoryMode() { + memorymode = true; + setmodevisibility(false); 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, button_actwi]); @@ -353,6 +367,8 @@ function enterMemoryMode() { } function exitMemoryMode() { + memorymode = false; + setmodevisibility(adventure); hideMessage(); button_actmem.html("Memory"); show([button_actback, button_actretry, button_actedit, button_actwi]); @@ -391,12 +407,26 @@ function returnWiList(ar) { function dosubmit() { var txt = input_text.val(); - socket.send({'cmd': 'submit', 'data': txt}); + socket.send({'cmd': 'submit', 'actionmode': adventure ? action_mode : 0, 'data': txt}); input_text.val(""); hideMessage(); hidegenseqs(); } +function changemode() { + if(gamestarted) { + action_mode += 1; + action_mode %= 2; // Total number of action modes (Story and Action) + } else { + action_mode = 0; // Force "Story" mode if game is not started + } + + switch (action_mode) { + case 0: button_mode_label.html("Story"); break; + case 1: button_mode_label.html("Action"); break; + } +} + function newTextHighlight(ref) { ref.addClass("color_green"); ref.addClass("colorfade"); @@ -533,6 +563,23 @@ function hidegenseqs() { }); } +function setmodevisibility(state) { + if(state){ // Enabling + show([button_mode]); + $("#inputrow").addClass("show_mode"); + } else{ // Disabling + hide([button_mode]); + $("#inputrow").removeClass("show_mode"); + } +} + +function setadventure(state) { + adventure = state; + if(!memorymode){ + setmodevisibility(state); + } +} + //=================================================================// // READY/RUNTIME //=================================================================// @@ -553,6 +600,8 @@ $(document).ready(function(){ button_impaidg = $("#btn_impaidg"); button_settings = $('#btn_settings'); button_format = $('#btn_format'); + button_mode = $('#btnmode') + button_mode_label = $('#btnmode_label') button_send = $('#btnsend'); button_actedit = $('#btn_actedit'); button_actmem = $('#btn_actmem'); @@ -613,6 +662,12 @@ $(document).ready(function(){ format_menu.html(""); wi_menu.html(""); } else if(msg.cmd == "updatescreen") { + _gamestarted = gamestarted + gamestarted = msg.gamestarted; + if(_gamestarted != gamestarted) { + action_mode = 0; + changemode(); + } // Send game content to Game Screen game_text.html(msg.data); // Scroll to bottom of text @@ -795,6 +850,11 @@ $(document).ready(function(){ } else if(msg.cmd == "updateuseprompt") { // Update toggle state $("#setuseprompt").prop('checked', msg.data).change(); + } else if(msg.cmd == "updateadventure") { + // Update toggle state + $("#setadventure").prop('checked', msg.data).change(); + // Update adventure state + setadventure(msg.data); } }); @@ -808,6 +868,10 @@ $(document).ready(function(){ button_send.on("click", function(ev) { dosubmit(); }); + + button_mode.on("click", function(ev) { + changemode(); + }); button_actretry.on("click", function(ev) { socket.send({'cmd': 'retry', 'data': ''}); diff --git a/static/application.js.bak b/static/application.js.bak deleted file mode 100644 index 71c066d4..00000000 --- a/static/application.js.bak +++ /dev/null @@ -1,981 +0,0 @@ -//=================================================================// -// VARIABLES -//=================================================================// - -// Socket IO Object -var socket; - -// UI references for jQuery -var connect_status; -var button_newgame; -var button_rndgame; -var button_save; -var button_saveas; -var button_savetofile; -var button_load; -var button_import; -var button_importwi; -var button_impaidg; -var button_settings; -var button_format; -var button_send; -var button_actedit; -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; -var anote_slider; -var popup; -var popup_title; -var popup_content; -var popup_accept; -var popup_close; -var aidgpopup; -var aidgpromptnum; -var aidg_accept; -var aidg_close; -var saveaspopup; -var saveasinput; -var saveas_accept; -var saveas_close; -var saveasoverwrite; -var loadpopup; -var loadcontent; -var load_accept; -var load_close; -var nspopup; -var ns_accept; -var ns_close; -var rspopup; -var rs_accept; -var rs_close; -var seqselmenu; -var seqselcontents; - -// Key states -var shift_down = false; -var do_clear_ent = false; - -// Display vars -var allowtoggle = false; -var formatcount = 0; - -//=================================================================// -// METHODS -//=================================================================// - -function addSetting(ob) { - // Add setting block to Settings Menu - if(ob.uitype == "slider"){ - settings_menu.append("
\ -
\ -
\ - "+ob.label+" ?"+ob.tooltip+"\ -
\ -
\ - "+ob.default+"\ -
\ -
\ -
\ - \ -
\ -
\ -
\ - "+ob.min+"\ -
\ -
\ - "+ob.max+"\ -
\ -
\ -
"); - // Set references to HTML objects - var refin = $("#"+ob.id); - var reflb = $("#"+ob.id+"cur"); - window["setting_"+ob.id] = refin; // Is this still needed? - window["label_"+ob.id] = reflb; // Is this still needed? - // Add event function to input - refin.on("input", function () { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).val()}); - }); - } else if(ob.uitype == "toggle"){ - settings_menu.append("
\ - \ - "+ob.label+" \ - ?"+ob.tooltip+"\ -
"); - // Tell Bootstrap-Toggle to render the new checkbox - $("input[type=checkbox]").bootstrapToggle(); - $("#"+ob.id).on("change", function () { - if(allowtoggle) { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); - } - }); - } -} - -function addFormat(ob) { - // Check if we need to make a new column for this button - if(formatcount == 0) { - format_menu.append("
"); - } - // Get reference to the last child column - var ref = $("#formatmenu > div").last(); - // Add format block to Format Menu - ref.append("
\ - \ - "+ob.label+" \ - ?"+ob.tooltip+"\ -
"); - // Tell Bootstrap-Toggle to render the new checkbox - $("input[type=checkbox]").bootstrapToggle(); - // Add event to input - $("#"+ob.id).on("change", function () { - if(allowtoggle) { - socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')}); - } - }); - // Increment display variable - formatcount++; - if(formatcount == 2) { - formatcount = 0; - } -} - -function addImportLine(ob) { - popup_content.append("
\ -
"+ob.title+"
\ -
"+ob.acts+"
\ -
"+ob.descr+"
\ -
"); - $("#import"+ob.num).on("click", function () { - socket.send({'cmd': 'importselect', 'data': $(this).attr('id')}); - highlightImportLine($(this)); - }); -} - -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"); - enableButtons([popup_accept]); -} - -function enableButtons(refs) { - for(i=0; i"); -} - -function hideWaitAnimation() { - $('#waitanim').remove(); -} - -function hide(refs) { - for(i=0; i *', function() { - editModeSelect($(this).attr("n")); - }); - disableSendBtn(); - hide([button_actback, button_actmem, button_actretry, button_actwi]); - show([button_delete]); -} - -function exitEditMode() { - // Remove class to each story chunk - hideMessage(); - button_actedit.html("Edit"); - game_text.children('chunk').removeClass("chunkhov"); - game_text.off('click', '> *'); - enableSendBtn(); - show([button_actback, button_actmem, button_actretry, button_actwi]); - hide([button_delete]); - input_text.val(""); -} - -function editModeSelect(n) { - socket.send({'cmd': 'editline', 'data': 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, button_actwi]); - // Display Author's Note field - anote_menu.slideDown("fast"); -} - -function exitMemoryMode() { - hideMessage(); - button_actmem.html("Memory"); - 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; i\ -
"+ar[i].name+"
\ -
"+ar[i].actions+"
\ - "); - $("#load"+i).on("click", function () { - enableButtons([load_accept]); - socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")}); - highlightLoadLine($(this)); - }); - } -} - -function highlightLoadLine(ref) { - $("#loadlistcontent > div").removeClass("popuplistselected"); - ref.addClass("popuplistselected"); -} - -function showNewStoryPopup() { - nspopup.removeClass("hidden"); - nspopup.addClass("flex"); -} - -function hideNewStoryPopup() { - nspopup.removeClass("flex"); - nspopup.addClass("hidden"); -} - -function showRandomStoryPopup() { - rspopup.removeClass("hidden"); - rspopup.addClass("flex"); -} - -function hideRandomStoryPopup() { - rspopup.removeClass("flex"); - rspopup.addClass("hidden"); -} - -function setStartState() { - enableSendBtn(); - 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(); - hideWaitAnimation(); - button_actedit.html("Edit"); - button_actmem.html("Memory"); - button_actwi.html("W Info"); - hideAidgPopup(); - hideSaveAsPopup(); - hideLoadPopup(); - hideNewStoryPopup(); - hidegenseqs(); -} - -function parsegenseqs(seqs) { - seqselcontents.html(""); - var i; - for(i=0; i"+seqs[i].generated_text+""); - $("#seqsel"+i).on("click", function () { - socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")}); - }); - } - $('#seqselmenu').slideDown("slow"); -} - -function hidegenseqs() { - $('#seqselmenu').slideUp("slow", function() { - seqselcontents.html(""); - }); -} - -//=================================================================// -// READY/RUNTIME -//=================================================================// - -$(document).ready(function(){ - - // Bind UI references - connect_status = $('#connectstatus'); - button_newgame = $('#btn_newgame'); - button_rndgame = $('#btn_rndgame'); - button_save = $('#btn_save'); - button_saveas = $('#btn_saveas'); - button_savetofile = $('#btn_savetofile'); - button_load = $('#btn_load'); - button_loadfrfile = $('#btn_loadfromfile'); - button_import = $("#btn_import"); - button_importwi = $("#btn_importwi"); - button_impaidg = $("#btn_impaidg"); - button_settings = $('#btn_settings'); - button_format = $('#btn_format'); - button_send = $('#btnsend'); - button_actedit = $('#btn_actedit'); - button_actmem = $('#btn_actmem'); - button_actback = $('#btn_actundo'); - button_actretry = $('#btn_actretry'); - button_delete = $('#btn_delete'); - button_actwi = $('#btn_actwi'); - game_text = $('#gametext'); - input_text = $('#input_text'); - message_text = $('#messagefield'); - settings_menu = $("#settingsmenu"); - format_menu = $('#formatmenu'); - anote_menu = $('#anoterowcontainer'); - wi_menu = $('#wimenu'); - anote_input = $('#anoteinput'); - anote_labelcur = $('#anotecur'); - anote_slider = $('#anotedepth'); - popup = $("#popupcontainer"); - popup_title = $("#popuptitletext"); - popup_content = $("#popupcontent"); - popup_accept = $("#btn_popupaccept"); - popup_close = $("#btn_popupclose"); - aidgpopup = $("#aidgpopupcontainer"); - aidgpromptnum = $("#aidgpromptnum"); - aidg_accept = $("#btn_aidgpopupaccept"); - aidg_close = $("#btn_aidgpopupclose"); - saveaspopup = $("#saveascontainer"); - saveasinput = $("#savename"); - saveas_accept = $("#btn_saveasaccept"); - saveas_close = $("#btn_saveasclose"); - saveasoverwrite = $("#saveasoverwrite"); - loadpopup = $("#loadcontainer"); - loadcontent = $("#loadlistcontent"); - load_accept = $("#btn_loadaccept"); - load_close = $("#btn_loadclose"); - nspopup = $("#newgamecontainer"); - ns_accept = $("#btn_nsaccept"); - ns_close = $("#btn_nsclose"); - rspopup = $("#rndgamecontainer"); - rs_accept = $("#btn_rsaccept"); - rs_close = $("#btn_rsclose"); - seqselmenu = $("#seqselmenu"); - seqselcontents = $("#seqselcontents"); - - // Connect to SocketIO server - loc = window.document.location; - socket = io.connect(loc.href); - - socket.on('from_server', function(msg) { - if(msg.cmd == "connected") { - // Connected to Server Actions - connect_status.html("Connected to KoboldAI Process!"); - connect_status.removeClass("color_orange"); - connect_status.addClass("color_green"); - // 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); - // Scroll to bottom of text - setTimeout(function () { - $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); - }, 5); - } else if(msg.cmd == "setgamestate") { - // Enable or Disable buttons - if(msg.data == "ready") { - enableSendBtn(); - enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); - hideWaitAnimation(); - } else if(msg.data == "wait") { - disableSendBtn(); - disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); - showWaitAnimation(); - } else if(msg.data == "start") { - setStartState(); - } - } else if(msg.cmd == "editmode") { - // Enable or Disable edit mode - if(msg.data == "true") { - enterEditMode(); - } else { - exitEditMode(); - } - } else if(msg.cmd == "setinputtext") { - // Set input box text for edit mode - input_text.val(msg.data); - } else if(msg.cmd == "enablesubmit") { - // Enables the submit button - enableSendBtn(); - } else if(msg.cmd == "memmode") { - // Enable or Disable memory edit mode - if(msg.data == "true") { - enterMemoryMode(); - } else { - exitMemoryMode(); - } - } else if(msg.cmd == "errmsg") { - // Send error message - errMessage(msg.data); - } else if(msg.cmd == "texteffect") { - // Apply color highlight to line of text - newTextHighlight($("#n"+msg.data)) - } else if(msg.cmd == "updatetemp") { - // Send current temp value to input - $("#settemp").val(parseFloat(msg.data)); - $("#settempcur").html(msg.data); - } else if(msg.cmd == "updatetopp") { - // Send current top p value to input - $("#settopp").val(parseFloat(msg.data)); - $("#settoppcur").html(msg.data); - } else if(msg.cmd == "updatereppen") { - // Send current rep pen value to input - $("#setreppen").val(parseFloat(msg.data)); - $("#setreppencur").html(msg.data); - } else if(msg.cmd == "updateoutlen") { - // Send current output amt value to input - $("#setoutput").val(parseInt(msg.data)); - $("#setoutputcur").html(msg.data); - } else if(msg.cmd == "updatetknmax") { - // Send current max tokens value to input - $("#settknmax").val(parseInt(msg.data)); - $("#settknmaxcur").html(msg.data); - } else if(msg.cmd == "updateikgen") { - // Send current max tokens value to input - $("#setikgen").val(parseInt(msg.data)); - $("#setikgencur").html(msg.data); - } else if(msg.cmd == "setlabeltemp") { - // Update setting label with value from server - $("#settempcur").html(msg.data); - } else if(msg.cmd == "setlabeltopp") { - // Update setting label with value from server - $("#settoppcur").html(msg.data); - } else if(msg.cmd == "setlabelreppen") { - // Update setting label with value from server - $("#setreppencur").html(msg.data); - } else if(msg.cmd == "setlabeloutput") { - // Update setting label with value from server - $("#setoutputcur").html(msg.data); - } else if(msg.cmd == "setlabeltknmax") { - // Update setting label with value from server - $("#settknmaxcur").html(msg.data); - } else if(msg.cmd == "setlabelikgen") { - // Update setting label with value from server - $("#setikgencur").html(msg.data); - } else if(msg.cmd == "updateanotedepth") { - // Send current Author's Note depth value to input - anote_slider.val(parseInt(msg.data)); - anote_labelcur.html(msg.data); - } else if(msg.cmd == "setlabelanotedepth") { - // Update setting label with value from server - anote_labelcur.html(msg.data); - } else if(msg.cmd == "getanote") { - // Request contents of Author's Note field - var txt = anote_input.val(); - socket.send({'cmd': 'anote', 'data': txt}); - } else if(msg.cmd == "setanote") { - // Set contents of Author's Note field - anote_input.val(msg.data); - } else if(msg.cmd == "addsetting") { - // Add setting controls - addSetting(msg.data); - } else if(msg.cmd == "addformat") { - // Add setting controls - addFormat(msg.data); - } else if(msg.cmd == "updatefrmttriminc") { - // Update toggle state - $("#frmttriminc").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtrmblln") { - // Update toggle state - $("#frmtrmblln").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtrmspch") { - // Update toggle state - $("#frmtrmspch").prop('checked', msg.data).change(); - } else if(msg.cmd == "updatefrmtadsnsp") { - // Update toggle state - $("#frmtadsnsp").prop('checked', msg.data).change(); - } else if(msg.cmd == "allowtoggle") { - // Allow toggle change states to propagate - allowtoggle = msg.data; - } else if(msg.cmd == "popupshow") { - // Show/Hide Popup - popupShow(msg.data); - } else if(msg.cmd == "addimportline") { - // Add import popup entry - addImportLine(msg.data); - } 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); - } else if(msg.cmd == "saveas") { - // Show Save As prompt - showSaveAsPopup(); - } else if(msg.cmd == "hidesaveas") { - // Hide Save As prompt - hideSaveAsPopup(); - } else if(msg.cmd == "buildload") { - // Send array of save files to load UI - buildLoadList(msg.data); - } else if(msg.cmd == "askforoverwrite") { - // Show overwrite warning - show([saveasoverwrite]); - } else if(msg.cmd == "genseqs") { - // Parse generator sequences to UI - parsegenseqs(msg.data); - } else if(msg.cmd == "hidegenseqs") { - // Collapse genseqs menu - hidegenseqs(); - } else if(msg.cmd == "setlabelnumseq") { - // Update setting label with value from server - $("#setnumseqcur").html(msg.data); - } else if(msg.cmd == "updatenumseq") { - // Send current max tokens value to input - $("#setnumseq").val(parseInt(msg.data)); - $("#setnumseqcur").html(msg.data); - } else if(msg.cmd == "setlabelwidepth") { - // Update setting label with value from server - $("#setwidepthcur").html(msg.data); - } else if(msg.cmd == "updatewidepth") { - // Send current max tokens value to input - $("#setwidepth").val(parseInt(msg.data)); - $("#setwidepthcur").html(msg.data); - } else if(msg.cmd == "updateuseprompt") { - // Update toggle state - $("#setuseprompt").prop('checked', msg.data).change(); - } - }); - - socket.on('disconnect', function() { - connect_status.html("Lost connection..."); - connect_status.removeClass("color_green"); - connect_status.addClass("color_orange"); - }); - - // Bind actions to UI buttons - button_send.on("click", function(ev) { - dosubmit(); - }); - - button_actretry.on("click", function(ev) { - socket.send({'cmd': 'retry', 'data': ''}); - hidegenseqs(); - }); - - button_actback.on("click", function(ev) { - socket.send({'cmd': 'back', 'data': ''}); - hidegenseqs(); - }); - - button_actedit.on("click", function(ev) { - socket.send({'cmd': 'edit', 'data': ''}); - }); - - button_delete.on("click", function(ev) { - socket.send({'cmd': 'delete', 'data': ''}); - }); - - button_actmem.on("click", function(ev) { - socket.send({'cmd': 'memory', 'data': ''}); - }); - - button_savetofile.on("click", function(ev) { - socket.send({'cmd': 'savetofile', 'data': ''}); - }); - - button_loadfrfile.on("click", function(ev) { - socket.send({'cmd': 'loadfromfile', 'data': ''}); - }); - - button_import.on("click", function(ev) { - socket.send({'cmd': 'import', 'data': ''}); - }); - - button_importwi.on("click", function(ev) { - socket.send({'cmd': 'importwi', 'data': ''}); - }); - - button_settings.on("click", function(ev) { - $('#settingsmenu').slideToggle("slow"); - }); - - button_format.on("click", function(ev) { - $('#formatmenu').slideToggle("slow"); - }); - - popup_close.on("click", function(ev) { - socket.send({'cmd': 'importcancel', 'data': ''}); - }); - - popup_accept.on("click", function(ev) { - socket.send({'cmd': 'importaccept', 'data': ''}); - }); - - button_actwi.on("click", function(ev) { - socket.send({'cmd': 'wi', 'data': ''}); - }); - - button_impaidg.on("click", function(ev) { - showAidgPopup(); - }); - - aidg_close.on("click", function(ev) { - hideAidgPopup(); - }); - - aidg_accept.on("click", function(ev) { - sendAidgImportRequest(); - }); - - button_save.on("click", function(ev) { - socket.send({'cmd': 'saverequest', 'data': ''}); - }); - - button_saveas.on("click", function(ev) { - showSaveAsPopup(); - }); - - saveas_close.on("click", function(ev) { - hideSaveAsPopup(); - socket.send({'cmd': 'clearoverwrite', 'data': ''}); - }); - - saveas_accept.on("click", function(ev) { - sendSaveAsRequest(); - }); - - button_load.on("click", function(ev) { - socket.send({'cmd': 'loadlistrequest', 'data': ''}); - }); - - load_close.on("click", function(ev) { - hideLoadPopup(); - }); - - load_accept.on("click", function(ev) { - socket.send({'cmd': 'loadrequest', 'data': ''}); - hideLoadPopup(); - }); - - button_newgame.on("click", function(ev) { - showNewStoryPopup(); - }); - - ns_accept.on("click", function(ev) { - socket.send({'cmd': 'newgame', 'data': ''}); - hideNewStoryPopup(); - }); - - ns_close.on("click", function(ev) { - hideNewStoryPopup(); - }); - - button_rndgame.on("click", function(ev) { - showRandomStoryPopup(); - }); - - rs_accept.on("click", function(ev) { - socket.send({'cmd': 'rndgame', 'data': ''}); - hideRandomStoryPopup(); - }); - - rs_close.on("click", function(ev) { - hideRandomStoryPopup(); - }); - - anote_slider.on("input", function () { - socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); - }); - - saveasinput.on("input", function () { - if(saveasinput.val() == "") { - disableButtons([saveas_accept]); - } else { - enableButtons([saveas_accept]); - } - hide([saveasoverwrite]); - }); - - // Bind Enter button to submit - input_text.keydown(function (ev) { - if (ev.which == 13 && !shift_down) { - do_clear_ent = true; - dosubmit(); - } else if(ev.which == 16) { - shift_down = true; - } - }); - - // Enter to submit, but not if holding shift - input_text.keyup(function (ev) { - if (ev.which == 13 && do_clear_ent) { - input_text.val(""); - do_clear_ent = false; - } else if(ev.which == 16) { - shift_down = false; - } - }); - - aidgpromptnum.keydown(function (ev) { - if (ev.which == 13) { - sendAidgImportRequest(); - } - }); - - saveasinput.keydown(function (ev) { - if (ev.which == 13 && saveasinput.val() != "") { - sendSaveAsRequest(); - } - }); -}); - diff --git a/static/custom.css b/static/custom.css index f27bce24..cf21ed06 100644 --- a/static/custom.css +++ b/static/custom.css @@ -6,6 +6,11 @@ chunk { color: #ffffff; } +action { + color: #9ff7fa; + font-weight: bold; +} + #topmenu { background-color: #337ab7; padding: 10px; @@ -97,12 +102,21 @@ chunk { margin-left: 20px; } +#inputrow.show_mode { + grid-template-columns: 7% 83% 10%; +} + #inputrow { margin-top: 10px; padding: 0px; width: 100%; display: grid; - grid-template-columns: 90% 10%; + grid-template-columns: 0% 90% 10%; +} + +#inputrowmode { + position: relative; + padding-right: 0px; } #inputrowleft { @@ -121,6 +135,13 @@ chunk { color: #ffffff; } +#btnmode { + width: 100%; + height: 100%; + overflow: auto; + overflow-x: hidden; +} + #btnsend { width: 100%; height: 100%; @@ -302,10 +323,15 @@ chunk { cursor: pointer; } +.chunkhov:hover > action { + color: #00fa00; +} + .colorfade { -moz-transition:color 1s ease-in; -o-transition:color 1s ease-in; -webkit-transition:color 1s ease-in; + transition:color 1s ease-in; } .color_orange { @@ -448,6 +474,7 @@ chunk { -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; } .loadlistitem:hover { @@ -525,6 +552,7 @@ chunk { -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; } .popuplistitem:hover { @@ -575,6 +603,7 @@ chunk { -moz-transition: all 0.15s ease-in; -o-transition: all 0.15s ease-in; -webkit-transition: all 0.15s ease-in; + transition: all 0.15s ease-in; } .seqselitem:hover { diff --git a/templates/index.html b/templates/index.html index a334f905..4b9ac8f7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -101,6 +101,9 @@
+
+ +
diff --git a/utils.py b/utils.py index fd46b8db..afdf29bc 100644 --- a/utils.py +++ b/utils.py @@ -59,8 +59,11 @@ def replaceblanklines(txt): #==================================================================# # #==================================================================# -def removespecialchars(txt): - txt = re.sub(r"[#/@%<>{}+=~|\^]", "", txt) +def removespecialchars(txt, vars=None): + if vars is None or vars.actionmode == 0: + txt = re.sub(r"[#/@%<>{}+=~|\^]", "", txt) + else: + txt = re.sub(r"[#/@%{}+=~|\^]", "", txt) return txt #==================================================================# From 33215a87b326ac5e443cd81c2cbf60ef5bda2dbc Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 13:48:33 +0200 Subject: [PATCH 011/117] Added VE_FORBRYDERNE's Selective World Info This update allows you to selectively choose when World Info is loaded for more control and RAM savings. --- .gitignore | 1 + aiserver.py | 51 ++++++++++++++++++----- static/application.js | 94 ++++++++++++++++++++++++++++++++++--------- static/custom.css | 10 +++-- 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 8a6b218c..9f4241cd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ client.settings stories/* !stories/sample_story.json /.project +*.bak \ No newline at end of file diff --git a/aiserver.py b/aiserver.py index cb282e5b..8db499a1 100644 --- a/aiserver.py +++ b/aiserver.py @@ -506,6 +506,10 @@ def get_message(msg): addwiitem() elif(msg['cmd'] == 'widelete'): deletewi(msg['data']) + elif(msg['cmd'] == 'wiselon'): + vars.worldinfo[msg['data']]["selective"] = True + elif(msg['cmd'] == 'wiseloff'): + vars.worldinfo[msg['data']]["selective"] = False elif(msg['cmd'] == 'sendwilist'): commitwi(msg['data']) elif(msg['cmd'] == 'aidgimport'): @@ -1240,7 +1244,7 @@ def togglewimode(): # #==================================================================# def addwiitem(): - ob = {"key": "", "content": "", "num": len(vars.worldinfo), "init": False} + ob = {"key": "", "keysecondary": "", "content": "", "num": len(vars.worldinfo), "init": False, "selective": False} vars.worldinfo.append(ob); emit('from_server', {'cmd': 'addwiitem', 'data': ob}) @@ -1291,8 +1295,10 @@ def organizewi(): #==================================================================# def commitwi(ar): for ob in ar: - vars.worldinfo[ob["num"]]["key"] = ob["key"] - vars.worldinfo[ob["num"]]["content"] = ob["content"] + vars.worldinfo[ob["num"]]["key"] = ob["key"] + vars.worldinfo[ob["num"]]["keysecondary"] = ob["keysecondary"] + vars.worldinfo[ob["num"]]["content"] = ob["content"] + vars.worldinfo[ob["num"]]["selective"] = ob["selective"] # Was this a deletion request? If so, remove the requested index if(vars.deletewi >= 0): del vars.worldinfo[vars.deletewi] @@ -1345,14 +1351,29 @@ def checkworldinfo(txt): if(wi["key"] != ""): # Split comma-separated keys keys = wi["key"].split(",") + keys_secondary = wi.get("keysecondary", "").split(",") + for k in keys: ky = k # Remove leading/trailing spaces if the option is enabled if(vars.wirmvwhtsp): ky = k.strip() if ky in txt: - wimem = wimem + wi["content"] + "\n" - break + if wi.get("selective", False) and len(keys_secondary): + found = False + for ks in keys_secondary: + ksy = ks + if(vars.wirmvwhtsp): + ksy = ks.strip() + if ksy in txt: + wimem = wimem + wi["content"] + "\n" + found = True + break + if found: + break + else: + wimem = wimem + wi["content"] + "\n" + break return wimem @@ -1549,7 +1570,9 @@ def saveRequest(savpath): if(wi["key"] != ""): js["worldinfo"].append({ "key": wi["key"], - "content": wi["content"] + "keysecondary": wi["keysecondary"], + "content": wi["content"], + "selective": wi["selective"] }) # Write it @@ -1606,9 +1629,11 @@ def loadRequest(loadpath): for wi in js["worldinfo"]: vars.worldinfo.append({ "key": wi["key"], + "keysecondary": wi.get("keysecondary", ""), "content": wi["content"], "num": num, - "init": True + "init": True, + "selective": wi.get("selective", False) }) num += 1 @@ -1722,9 +1747,11 @@ def importgame(): for wi in ref["worldInfo"]: vars.worldinfo.append({ "key": wi["keys"], + "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], "num": num, - "init": True + "init": True, + "selective": wi.get("selective", False) }) num += 1 @@ -1766,9 +1793,11 @@ def importAidgRequest(id): for wi in js["worldInfos"]: vars.worldinfo.append({ "key": wi["keys"], + "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], "num": num, - "init": True + "init": True, + "selective": wi.get("selective", False) }) num += 1 @@ -1797,9 +1826,11 @@ def wiimportrequest(): for wi in js: vars.worldinfo.append({ "key": wi["keys"], + "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], "num": num, - "init": True + "init": True, + "selective": wi.get("selective", False) }) num += 1 diff --git a/static/application.js b/static/application.js index 5a01f289..2b9bd2db 100644 --- a/static/application.js +++ b/static/application.js @@ -178,21 +178,51 @@ function addImportLine(ob) { function addWiLine(ob) { if(ob.init) { - wi_menu.append("
\ -
\ - \ - \ - \ -
\ -
\ - \ -
\ -
\ - \ -
\ -
"); + if(ob.selective){ + wi_menu.append("
\ +
\ + \ + \ + \ +
\ +
\ + \ + \ + \ +
\ +
\ + \ +
\ +
\ + \ + \ +
\ +
"); + } else { + wi_menu.append("
\ +
\ + \ + \ + \ +
\ +
\ + \ + \ + \ +
\ +
\ + \ +
\ +
\ + \ + \ +
\ +
"); + } // Send key value to text input $("#wikey"+ob.num).val(ob.key); + $("#wikeyprimary"+ob.num).val(ob.key); + $("#wikeysecondary"+ob.num).val(ob.keysecondary); // Assign delete event to button $("#btn_wi"+ob.num).on("click", function () { showWiDeleteConfirm(ob.num); @@ -206,11 +236,17 @@ function addWiLine(ob) { \
\
\ - \ + \ + \ + \
\
\ \
\ +
\ + \ + \ +
\
"); // Assign function to expand WI item to button $("#btn_wi"+ob.num).on("click", function () { @@ -224,10 +260,16 @@ function addWiLine(ob) { $("#btn_widel"+ob.num).on("click", function () { socket.send({'cmd': 'widelete', 'data': ob.num}); }); + $("#btn_wiselon"+ob.num).on("click", function () { + enableWiSelective(ob.num); + }); + $("#btn_wiseloff"+ob.num).on("click", function () { + disableWiSelective(ob.num); + }); } function expandWiLine(num) { - show([$("#wikey"+num), $("#wientry"+num)]); + show([$("#wikey"+num), $("#wientry"+num), $("#btn_wiselon"+num)]); $("#btn_wi"+num).html("X"); $("#btn_wi"+num).off(); // Tell server the WI entry was initialized @@ -247,6 +289,20 @@ function hideWiDeleteConfirm(num) { hide([$("#btn_widel"+num), $("#btn_wican"+num)]); } +function enableWiSelective(num) { + hide([$("#btn_wiselon"+num), $("#wikey"+num)]); + // Tell server the WI entry is now selective + socket.send({'cmd': 'wiselon', 'data': num}); + show([$("#wikeyprimary"+num), $("#wikeysecondary"+num), $("#btn_wiseloff"+num)]); +} + +function disableWiSelective(num) { + hide([$("#btn_wiseloff"+num), $("#wikeyprimary"+num), $("#wikeysecondary"+num)]); + // Tell server the WI entry is now non-selective + socket.send({'cmd': 'wiseloff', 'data': num}); + show([$("#btn_wiselon"+num), $("#wikey"+num)]); +} + function highlightImportLine(ref) { $("#popupcontent > div").removeClass("popuplistselected"); ref.addClass("popuplistselected"); @@ -397,9 +453,11 @@ function returnWiList(ar) { var list = []; var i; for(i=0; i input { - height: 100%; background-color: #404040; color: #ffffff; } @@ -686,4 +686,8 @@ action { width: 80%; overflow: hidden; font-size: 12pt; -} \ No newline at end of file +} + +.wiselective > button { + white-space: normal; +} From caee12eae0e7acf01f3e9c67721e896737af49af Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 13:55:59 +0200 Subject: [PATCH 012/117] Updated credits Forgot to give credit for Selective World Info, this is now fixed. --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 7b4d93c4..5ea508e4 100644 --- a/readme.txt +++ b/readme.txt @@ -10,4 +10,4 @@ Looking for ColabKobold? Check the different branches on my account. Contains work from : - All the original KoboldAI creators - Henky!! (Random Story Generator / Setting Optimizations) -- VE_FORBRYDERNE (Adventure Mode) \ No newline at end of file +- VE_FORBRYDERNE (Adventure Mode, Selective World Info) \ No newline at end of file From 72bfc417da2451d10c7970d262c2a4cdf0315e8e Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 14:47:57 +0200 Subject: [PATCH 013/117] top_k and tfs support by Frogging101 Adds top_k and tfs support, also fixes a SocketIO error. --- aiserver.py | 28 ++++++++++++++++++++++++- gensettings.py | 48 +++++++++++++++++++++++++++++++++++++++++-- static/application.js | 17 +++++++++++++-- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/aiserver.py b/aiserver.py index 8db499a1..bb6965b8 100644 --- a/aiserver.py +++ b/aiserver.py @@ -64,6 +64,8 @@ class vars: rep_pen = 1.1 # Default generator repetition_penalty temp = 0.5 # Default generator temperature top_p = 0.9 # Default generator top_p + top_k = 0 # Default generator top_k + tfs = 0.0 # Default generator tfs (tail-free sampling) numseqs = 1 # Number of sequences to ask the generator to create gamestarted = False # Whether the game has started (disables UI elements) prompt = "" # Prompt @@ -449,6 +451,14 @@ def get_message(msg): vars.top_p = float(msg['data']) emit('from_server', {'cmd': 'setlabeltopp', 'data': msg['data']}) settingschanged() + elif(msg['cmd'] == 'settopk'): + vars.top_k = int(msg['data']) + emit('from_server', {'cmd': 'setlabeltopk', 'data': msg['data']}) + settingschanged() + elif(msg['cmd'] == 'settfs'): + vars.tfs = float(msg['data']) + emit('from_server', {'cmd': 'setlabeltfs', 'data': msg['data']}) + settingschanged() elif(msg['cmd'] == 'setreppen'): vars.rep_pen = float(msg['data']) emit('from_server', {'cmd': 'setlabelreppen', 'data': msg['data']}) @@ -588,6 +598,8 @@ def savesettings(): js["andepth"] = vars.andepth js["temp"] = vars.temp js["top_p"] = vars.top_p + js["top_k"] = vars.top_k + js["tfs"] = vars.tfs js["rep_pen"] = vars.rep_pen js["genamt"] = vars.genamt js["max_length"] = vars.max_length @@ -623,6 +635,10 @@ def loadsettings(): vars.temp = js["temp"] if("top_p" in js): vars.top_p = js["top_p"] + if("top_k" in js): + vars.top_k = js["top_k"] + if("tfs" in js): + vars.tfs = js["tfs"] if("rep_pen" in js): vars.rep_pen = js["rep_pen"] if("genamt" in js): @@ -922,13 +938,19 @@ def generate(txt, min, max): # Submit input text to generator try: + top_p = vars.top_p if vars.top_p > 0.0 else None + top_k = vars.top_k if vars.top_k > 0 else None + tfs = vars.tfs if vars.tfs > 0.0 else None + genout = generator( txt, do_sample=True, min_length=min, max_length=max, repetition_penalty=vars.rep_pen, - top_p=vars.top_p, + top_p=top_p, + top_k=top_k, + tfs=tfs, temperature=vars.temp, bad_words_ids=vars.badwordsids, use_cache=True, @@ -1016,6 +1038,8 @@ def sendtocolab(txt, min, max): 'rep_pen': vars.rep_pen, 'temperature': vars.temp, 'top_p': vars.top_p, + 'top_k': vars.top_k, + 'tfs': vars.tfs, 'numseqs': vars.numseqs, 'retfultxt': False } @@ -1139,6 +1163,8 @@ def refresh_settings(): if(vars.model != "InferKit"): emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp}) emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p}) + emit('from_server', {'cmd': 'updatetopk', 'data': vars.top_k}) + emit('from_server', {'cmd': 'updatetfs', 'data': vars.tfs}) emit('from_server', {'cmd': 'updatereppen', 'data': vars.rep_pen}) emit('from_server', {'cmd': 'updateoutlen', 'data': vars.genamt}) emit('from_server', {'cmd': 'updatetknmax', 'data': vars.max_length}) diff --git a/gensettings.py b/gensettings.py index da3aaa76..ac531ba2 100644 --- a/gensettings.py +++ b/gensettings.py @@ -14,11 +14,33 @@ gensettingstf = [{ "unit": "float", "label": "Top p Sampling", "id": "settopp", - "min": 0.1, + "min": 0.0, "max": 1.0, "step": 0.05, "default": 0.9, "tooltip": "Used to discard unlikely text in the sampling process. Lower values will make text more predictable but can become repetitious." + }, + { + "uitype": "slider", + "unit": "int", + "label": "Top k Sampling", + "id": "settopk", + "min": 0, + "max": 100, + "step": 1, + "default": 0, + "tooltip": "Alternative sampling method, can be combined with top_p." + }, + { + "uitype": "slider", + "unit": "float", + "label": "Tail-free Sampling", + "id": "settfs", + "min": 0.0, + "max": 1.0, + "step": 0.05, + "default": 0.0, + "tooltip": "Alternative sampling method; it is recommended to disable (set to 0) top_p and top_k if using this. 0.95 is thought to be a good value." }, { "uitype": "slider", @@ -114,12 +136,34 @@ gensettingsik =[{ "unit": "float", "label": "Top p Sampling", "id": "settopp", - "min": 0.1, + "min": 0.0, "max": 1.0, "step": 0.05, "default": 1.1, "tooltip": "Used to discard unlikely text in the sampling process. Lower values will make text more predictable but can become repetitious." }, + { + "uitype": "slider", + "unit": "int", + "label": "Top k Sampling", + "id": "settopk", + "min": 0, + "max": 100, + "step": 1, + "default": 0, + "tooltip": "Alternative sampling method, can be combined with top_p." + }, + { + "uitype": "slider", + "unit": "float", + "label": "Tail-free Sampling", + "id": "settfs", + "min": 0.0, + "max": 1.0, + "step": 0.05, + "default": 0.0, + "tooltip": "Alternative sampling method; it is recommended to disable (set to 0) top_p and top_k if using this. 0.95 is thought to be a good value." + }, { "uitype": "slider", "unit": "int", diff --git a/static/application.js b/static/application.js index 2b9bd2db..4de2409d 100644 --- a/static/application.js +++ b/static/application.js @@ -706,8 +706,7 @@ $(document).ready(function(){ seqselcontents = $("#seqselcontents"); // Connect to SocketIO server - loc = window.document.location; - socket = io.connect(loc.href); + socket = io.connect(window.document.origin); socket.on('from_server', function(msg) { if(msg.cmd == "connected") { @@ -779,6 +778,14 @@ $(document).ready(function(){ // Send current top p value to input $("#settopp").val(parseFloat(msg.data)); $("#settoppcur").html(msg.data); + } else if(msg.cmd == "updatetopk") { + // Send current top k value to input + $("#settopk").val(parseFloat(msg.data)); + $("#settopkcur").html(msg.data); + } else if(msg.cmd == "updatetfs") { + // Send current tfs value to input + $("#settfs").val(parseFloat(msg.data)); + $("#settfscur").html(msg.data); } else if(msg.cmd == "updatereppen") { // Send current rep pen value to input $("#setreppen").val(parseFloat(msg.data)); @@ -801,6 +808,12 @@ $(document).ready(function(){ } else if(msg.cmd == "setlabeltopp") { // Update setting label with value from server $("#settoppcur").html(msg.data); + } else if(msg.cmd == "setlabeltopk") { + // Update setting label with value from server + $("#settopkcur").html(msg.data); + } else if(msg.cmd == "setlabeltfs") { + // Update setting label with value from server + $("#settfscur").html(msg.data); } else if(msg.cmd == "setlabelreppen") { // Update setting label with value from server $("#setreppencur").html(msg.data); From a0ed12a74aa7e9f9412b2a98f5f538d39c29bd4c Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 14:50:07 +0200 Subject: [PATCH 014/117] Update readme.txt --- readme.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index 5ea508e4..3e3afc8c 100644 --- a/readme.txt +++ b/readme.txt @@ -10,4 +10,5 @@ Looking for ColabKobold? Check the different branches on my account. Contains work from : - All the original KoboldAI creators - Henky!! (Random Story Generator / Setting Optimizations) -- VE_FORBRYDERNE (Adventure Mode, Selective World Info) \ No newline at end of file +- VE_FORBRYDERNE (Adventure Mode, Selective World Info) +- Frogging101 (top_k / tfs support) \ No newline at end of file From 9760c159fd491cf1e3af4455cd0752d4854782b0 Mon Sep 17 00:00:00 2001 From: henk717 Date: Thu, 19 Aug 2021 16:20:41 +0200 Subject: [PATCH 015/117] Allow using Jupyter inside the KoboldAI environment Not used by the game, but can be useful for running and troubleshooting notebook features. --- notebook.bat | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 notebook.bat diff --git a/notebook.bat b/notebook.bat new file mode 100644 index 00000000..b5b44948 --- /dev/null +++ b/notebook.bat @@ -0,0 +1,20 @@ +@echo off +cd %~dp0 +TITLE Jupyter for KoboldAI Runtime +SET /P M=nul +umamba.exe install --no-shortcuts -r K:\python\ -n base -c conda-forge jupyter +call K:\python\condabin\activate +jupyter notebook +subst K: /D +cmd /k \ No newline at end of file From 136dd711719db22a9ffba9331454f5518b9fcd74 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 00:37:59 +0200 Subject: [PATCH 016/117] Added --remote Mode First step towards native Colab support, built in Cloudflare tunnels easily allows players to play KoboldAI on another device. This mode also removes buttons that would get you stuck if you have no local PC access. --- aiserver.py | 23 ++++++++++++++++++++++- environments/finetuneanon.yml | 3 ++- environments/huggingface.yml | 5 ++++- play.bat | 4 ++-- requirements.txt | 3 ++- static/application.js | 2 ++ 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/aiserver.py b/aiserver.py index bb6965b8..4fa62615 100644 --- a/aiserver.py +++ b/aiserver.py @@ -12,6 +12,7 @@ from tkinter import messagebox import json import requests import html +import argparse # KoboldAI import fileops @@ -103,6 +104,7 @@ class vars: acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses) actionmode = 1 adventure = False + remote = False #==================================================================# # Function to get model selection at startup @@ -152,6 +154,14 @@ def gettokenids(char): # Startup #==================================================================# +# Parsing Parameters +parser = argparse.ArgumentParser(description="My Script") +parser.add_argument("--remote", action='store_true') +args = parser.parse_args() + +if args.remote: + vars.remote = True; + # Select a model to run print("{0}Welcome to the KoboldAI Client!\nSelect an AI model to continue:{1}\n".format(colors.CYAN, colors.END)) getModelSelection() @@ -373,6 +383,9 @@ def index(): def do_connect(): print("{0}Client connected!{1}".format(colors.GREEN, colors.END)) emit('from_server', {'cmd': 'connected'}) + if(vars.remote): + emit('from_server', {'cmd': 'runs_remotely'}) + if(not vars.gamestarted): setStartState() sendsettings() @@ -1900,10 +1913,18 @@ def randomGameRequest(topic): # Final startup commands to launch Flask app #==================================================================# if __name__ == "__main__": + # Load settings from client.settings loadsettings() # 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, host='0.0.0.0', port=5000) - socketio.run(app) + if(vars.remote): + from flask_cloudflared import start_cloudflared + start_cloudflared(5000) + socketio.run(app, host='0.0.0.0', port=5000) + else: + import webbrowser + webbrowser.open_new('http://localhost:5000') + socketio.run(app) \ No newline at end of file diff --git a/environments/finetuneanon.yml b/environments/finetuneanon.yml index 2ac0cc55..7a23b7a2 100644 --- a/environments/finetuneanon.yml +++ b/environments/finetuneanon.yml @@ -12,4 +12,5 @@ dependencies: - pip - git - pip: - - git+https://github.com/finetuneanon/transformers@gpt-neo-localattention3-rp-b \ No newline at end of file + - git+https://github.com/finetuneanon/transformers@gpt-neo-localattention3-rp-b + - flask-cloudflared \ No newline at end of file diff --git a/environments/huggingface.yml b/environments/huggingface.yml index bfbd33e5..659ecc74 100644 --- a/environments/huggingface.yml +++ b/environments/huggingface.yml @@ -9,4 +9,7 @@ dependencies: - flask-socketio - pytorch - tensorflow-gpu - - transformers \ No newline at end of file + - transformers + - pip + - pip: + - flask-cloudflared \ No newline at end of file diff --git a/play.bat b/play.bat index 2d723fd9..db05b27e 100644 --- a/play.bat +++ b/play.bat @@ -7,12 +7,12 @@ IF %M%==2 GOTO subfolder :subfolder call miniconda3\condabin\activate -python aiserver.py +python aiserver.py %* cmd /k :drivemap subst K: miniconda3 >nul call K:\python\condabin\activate -python aiserver.py +python aiserver.py %* subst K: /D cmd /k \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 043a9d4b..a17ea08f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ tensorflow-gpu Flask == 1.1.2 Flask-SocketIO == 5.0.1 requests == 2.25.1 -torch == 1.8.1 \ No newline at end of file +torch == 1.8.1 +flask-cloudflared \ No newline at end of file diff --git a/static/application.js b/static/application.js index 4de2409d..3963ed8a 100644 --- a/static/application.js +++ b/static/application.js @@ -926,6 +926,8 @@ $(document).ready(function(){ $("#setadventure").prop('checked', msg.data).change(); // Update adventure state setadventure(msg.data); + } else if(msg.cmd == "runs_remotely") { + hide([button_loadfrfile, button_savetofile, button_import, button_importwi]); } }); From ba20c3407c7d0596efc2aa93f99e48ff203c42e3 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 10:49:35 +0200 Subject: [PATCH 017/117] Command line support Added command line options for model selection, this makes it usable inside Google Colab or other unattended servers people might want to use/provide. --- .gitignore | 5 ++++- aiserver.py | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 9f4241cd..59605d83 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ client.settings stories/* !stories/sample_story.json /.project -*.bak \ No newline at end of file +*.bak +miniconda3/* +*.settings +__pycache__ \ No newline at end of file diff --git a/aiserver.py b/aiserver.py index 4fa62615..39023e39 100644 --- a/aiserver.py +++ b/aiserver.py @@ -155,16 +155,25 @@ def gettokenids(char): #==================================================================# # Parsing Parameters -parser = argparse.ArgumentParser(description="My Script") -parser.add_argument("--remote", action='store_true') +parser = argparse.ArgumentParser(description="KoboldAI Server") +parser.add_argument("--remote", action='store_true', help="Optimizes KoboldAI for Remote Play") +parser.add_argument("--model", help="Specify the Model Type to skip the Menu") +parser.add_argument("--path", help="Specify the Path for local models (For model NeoCustom or GPT2Custom)") args = parser.parse_args() +vars.model = args.model; if args.remote: vars.remote = True; # Select a model to run -print("{0}Welcome to the KoboldAI Client!\nSelect an AI model to continue:{1}\n".format(colors.CYAN, colors.END)) -getModelSelection() +if args.model: + print("Welcome to KoboldAI!\nYou have selected the following Model:", vars.model) + if args.path: + print("You have selected the following path for your Model :", args.path) + vars.custmodpth = args.path; +else: + print("{0}Welcome to the KoboldAI Client!\nSelect an AI model to continue:{1}\n".format(colors.CYAN, colors.END)) + getModelSelection() # If transformers model was selected & GPU available, ask to use CPU or GPU if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): From 99c5ff240c5d983e2057b4ea16db8aef2848a914 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 11:39:04 +0200 Subject: [PATCH 018/117] Command Line Part 2 Automated Colab and GPU selection --- aiserver.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/aiserver.py b/aiserver.py index 39023e39..ea9dff40 100644 --- a/aiserver.py +++ b/aiserver.py @@ -159,6 +159,7 @@ parser = argparse.ArgumentParser(description="KoboldAI Server") parser.add_argument("--remote", action='store_true', help="Optimizes KoboldAI for Remote Play") parser.add_argument("--model", help="Specify the Model Type to skip the Menu") parser.add_argument("--path", help="Specify the Path for local models (For model NeoCustom or GPT2Custom)") +parser.add_argument("--cpu", action='store_true', help="By default unattended launches are on the GPU use this option to force CPU usage.") args = parser.parse_args() vars.model = args.model; @@ -171,6 +172,8 @@ if args.model: if args.path: print("You have selected the following path for your Model :", args.path) vars.custmodpth = args.path; + vars.colaburl = args.path + "/request"; # Lets just use the same parameter to keep it simple + else: print("{0}Welcome to the KoboldAI Client!\nSelect an AI model to continue:{1}\n".format(colors.CYAN, colors.END)) getModelSelection() @@ -186,9 +189,17 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): else: print("{0}NOT FOUND!{1}".format(colors.YELLOW, colors.END)) - if(vars.hascuda): + if args.model: + if(vars.hascuda): + genselected = True + vars.usegpu = True + if(args.cpu): + vars.usegpu = False + elif(vars.hascuda): print("{0}Use GPU or CPU for generation?: (Default GPU){1}\n".format(colors.CYAN, colors.END)) print(" 1 - GPU\n 2 - CPU\n") + + if(vars.hascuda): genselected = False while(genselected == False): genselect = input("Mode> ") @@ -307,8 +318,9 @@ if(vars.model == "OAI"): # Ask for ngrok url if Google Colab was selected if(vars.model == "Colab"): - print("{0}Please enter the ngrok.io or trycloudflare.com URL displayed in Google Colab:{1}\n".format(colors.CYAN, colors.END)) - vars.colaburl = input("URL> ") + "/request" + if(vars.colaburl == ""): + print("{0}Please enter the ngrok.io or trycloudflare.com URL displayed in Google Colab:{1}\n".format(colors.CYAN, colors.END)) + vars.colaburl = input("URL> ") + "/request" if(vars.model == "ReadOnly"): vars.noai = True From dd77ac2f3acae68e36c2087228e3c75cd9f82db9 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 12:30:52 +0200 Subject: [PATCH 019/117] GPU detection bugfix --- aiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index ea9dff40..3f6d3afd 100644 --- a/aiserver.py +++ b/aiserver.py @@ -198,9 +198,9 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): elif(vars.hascuda): print("{0}Use GPU or CPU for generation?: (Default GPU){1}\n".format(colors.CYAN, colors.END)) print(" 1 - GPU\n 2 - CPU\n") + genselected = False if(vars.hascuda): - genselected = False while(genselected == False): genselect = input("Mode> ") if(genselect == ""): From f12e3576a8913a4130ca1d85c1ed3aa85d71aa29 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 15:32:02 +0200 Subject: [PATCH 020/117] Multiple Browser Session Syncing Multiplayer anyone? :D --- aiserver.py | 209 ++++++++++++++++++++++++++++------------------------ 1 file changed, 113 insertions(+), 96 deletions(-) diff --git a/aiserver.py b/aiserver.py index 3f6d3afd..2756d67c 100644 --- a/aiserver.py +++ b/aiserver.py @@ -421,15 +421,15 @@ def do_connect(): sendwi() if(vars.mode == "play"): if(not vars.aibusy): - emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) else: - emit('from_server', {'cmd': 'setgamestate', 'data': 'wait'}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'wait'}, broadcast=True) elif(vars.mode == "edit"): - emit('from_server', {'cmd': 'editmode', 'data': 'true'}) + emit('from_server', {'cmd': 'editmode', 'data': 'true'}, broadcast=True) elif(vars.mode == "memory"): - emit('from_server', {'cmd': 'memmode', 'data': 'true'}) + emit('from_server', {'cmd': 'memmode', 'data': 'true'}, broadcast=True) elif(vars.mode == "wi"): - emit('from_server', {'cmd': 'wimode', 'data': 'true'}) + emit('from_server', {'cmd': 'wimode', 'data': 'true'}, broadcast=True) #==================================================================# # Event triggered when browser SocketIO sends data to the server @@ -455,10 +455,10 @@ def get_message(msg): elif(msg['cmd'] == 'edit'): if(vars.mode == "play"): vars.mode = "edit" - emit('from_server', {'cmd': 'editmode', 'data': 'true'}) + emit('from_server', {'cmd': 'editmode', 'data': 'true'}, broadcast=True) elif(vars.mode == "edit"): vars.mode = "play" - emit('from_server', {'cmd': 'editmode', 'data': 'false'}) + emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True) # EditLine Action elif(msg['cmd'] == 'editline'): editrequest(int(msg['data'])) @@ -479,61 +479,74 @@ def get_message(msg): randomGameRequest(msg['data']) elif(msg['cmd'] == 'settemp'): vars.temp = float(msg['data']) - emit('from_server', {'cmd': 'setlabeltemp', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeltemp', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'settopp'): vars.top_p = float(msg['data']) - emit('from_server', {'cmd': 'setlabeltopp', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeltopp', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'settopk'): vars.top_k = int(msg['data']) - emit('from_server', {'cmd': 'setlabeltopk', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeltopk', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'settfs'): vars.tfs = float(msg['data']) - emit('from_server', {'cmd': 'setlabeltfs', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeltfs', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setreppen'): vars.rep_pen = float(msg['data']) - emit('from_server', {'cmd': 'setlabelreppen', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabelreppen', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setoutput'): vars.genamt = int(msg['data']) - emit('from_server', {'cmd': 'setlabeloutput', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeloutput', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'settknmax'): vars.max_length = int(msg['data']) - emit('from_server', {'cmd': 'setlabeltknmax', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabeltknmax', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setikgen'): vars.ikgen = int(msg['data']) - emit('from_server', {'cmd': 'setlabelikgen', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabelikgen', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() # Author's Note field update elif(msg['cmd'] == 'anote'): anotesubmit(msg['data']) # Author's Note depth update elif(msg['cmd'] == 'anotedepth'): vars.andepth = int(msg['data']) - emit('from_server', {'cmd': 'setlabelanotedepth', 'data': msg['data']}) - settingschanged() + emit('from_server', {'cmd': 'setlabelanotedepth', 'data': msg['data']}, broadcast=True) + settingschanged() + refresh_settings() # Format - Trim incomplete sentences elif(msg['cmd'] == 'frmttriminc'): if('frmttriminc' in vars.formatoptns): vars.formatoptns["frmttriminc"] = msg['data'] - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'frmtrmblln'): if('frmtrmblln' in vars.formatoptns): vars.formatoptns["frmtrmblln"] = msg['data'] - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'frmtrmspch'): if('frmtrmspch' in vars.formatoptns): vars.formatoptns["frmtrmspch"] = msg['data'] - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'frmtadsnsp'): if('frmtadsnsp' in vars.formatoptns): vars.formatoptns["frmtadsnsp"] = msg['data'] - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'importselect'): vars.importnum = int(msg["data"].replace("import", "")) elif(msg['cmd'] == 'importcancel'): @@ -576,17 +589,21 @@ def get_message(msg): elif(msg['cmd'] == 'setnumseq'): vars.numseqs = int(msg['data']) emit('from_server', {'cmd': 'setlabelnumseq', 'data': msg['data']}) - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setwidepth'): vars.widepth = int(msg['data']) emit('from_server', {'cmd': 'setlabelwidepth', 'data': msg['data']}) - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setuseprompt'): vars.useprompt = msg['data'] - settingschanged() + settingschanged() + refresh_settings() elif(msg['cmd'] == 'setadventure'): vars.adventure = msg['data'] - settingschanged() + settingschanged() + refresh_settings() refresh_story() elif(msg['cmd'] == 'importwi'): wiimportrequest() @@ -600,8 +617,8 @@ def setStartState(): txt = txt + "Please load a game or enter a prompt below to begin!" else: txt = txt + "Please load or import a story to read. There is no AI in this mode." - emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': txt}) - emit('from_server', {'cmd': 'setgamestate', 'data': 'start'}) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': txt}, broadcast=True) + emit('from_server', {'cmd': 'setgamestate', 'data': 'start'}, broadcast=True) #==================================================================# # Transmit applicable settings to SocketIO to build UI sliders/toggles @@ -730,7 +747,7 @@ def actionsubmit(data, actionmode=0): vars.prompt = data if(not vars.noai): # Clear the startup text from game screen - emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': 'Please wait, generating story...'}) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': 'Please wait, generating story...'}, broadcast=True) calcsubmit(data) # Run the first action through the generator else: refresh_story() @@ -992,7 +1009,7 @@ def generate(txt, min, max): num_return_sequences=vars.numseqs ) except Exception as e: - emit('from_server', {'cmd': 'errmsg', 'data': 'Error occured during generator call, please check console.'}) + emit('from_server', {'cmd': 'errmsg', 'data': 'Error occured during generator call, please check console.'}, broadcast=True) print("{0}{1}{2}".format(colors.RED, e, colors.END)) set_aibusy(0) return @@ -1020,7 +1037,7 @@ def genresult(genout): # Add formatted text to Actions array and refresh the game screen vars.actions.append(genout) refresh_story() - emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) + emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}, broadcast=True) #==================================================================# # Send generator sequences to the UI for selection @@ -1037,7 +1054,7 @@ def genselect(genout): vars.genseqs = genout # Send sequences to UI for selection - emit('from_server', {'cmd': 'genseqs', 'data': genout}) + emit('from_server', {'cmd': 'genseqs', 'data': genout}, broadcast=True) # Refresh story for any input text refresh_story() @@ -1050,8 +1067,8 @@ def selectsequence(n): return vars.actions.append(vars.genseqs[int(n)]["generated_text"]) refresh_story() - emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) - emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}) + emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}, broadcast=True) + emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True) vars.genseqs = [] #==================================================================# @@ -1115,7 +1132,7 @@ def sendtocolab(txt, min, max): else: errmsg = "Colab API Error: Failed to get a reply from the server. Please check the colab console." print("{0}{1}{2}".format(colors.RED, errmsg, colors.END)) - emit('from_server', {'cmd': 'errmsg', 'data': errmsg}) + emit('from_server', {'cmd': 'errmsg', 'data': errmsg}, broadcast=True) set_aibusy(0) @@ -1185,41 +1202,41 @@ def refresh_story(): if vars.adventure: # Add special formatting to adventure actions item = vars.acregex_ui.sub('\\1', html.escape(item)) text_parts.extend(('', item, '')) - emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': formatforhtml(''.join(text_parts))}) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': formatforhtml(''.join(text_parts))}, broadcast=True) #==================================================================# # Sends the current generator settings to the Game Menu #==================================================================# def refresh_settings(): # Suppress toggle change events while loading state - emit('from_server', {'cmd': 'allowtoggle', 'data': False}) + emit('from_server', {'cmd': 'allowtoggle', 'data': False}, broadcast=True) if(vars.model != "InferKit"): - emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp}) - emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p}) - emit('from_server', {'cmd': 'updatetopk', 'data': vars.top_k}) - emit('from_server', {'cmd': 'updatetfs', 'data': vars.tfs}) - emit('from_server', {'cmd': 'updatereppen', 'data': vars.rep_pen}) - emit('from_server', {'cmd': 'updateoutlen', 'data': vars.genamt}) - emit('from_server', {'cmd': 'updatetknmax', 'data': vars.max_length}) - emit('from_server', {'cmd': 'updatenumseq', 'data': vars.numseqs}) + emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp}, broadcast=True) + emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p}, broadcast=True) + emit('from_server', {'cmd': 'updatetopk', 'data': vars.top_k}, broadcast=True) + emit('from_server', {'cmd': 'updatetfs', 'data': vars.tfs}, broadcast=True) + emit('from_server', {'cmd': 'updatereppen', 'data': vars.rep_pen}, broadcast=True) + emit('from_server', {'cmd': 'updateoutlen', 'data': vars.genamt}, broadcast=True) + emit('from_server', {'cmd': 'updatetknmax', 'data': vars.max_length}, broadcast=True) + emit('from_server', {'cmd': 'updatenumseq', 'data': vars.numseqs}, broadcast=True) else: - emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp}) - emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p}) - emit('from_server', {'cmd': 'updateikgen', 'data': vars.ikgen}) + emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp}, broadcast=True) + emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p}, broadcast=True) + emit('from_server', {'cmd': 'updateikgen', 'data': vars.ikgen}, broadcast=True) - emit('from_server', {'cmd': 'updateanotedepth', 'data': vars.andepth}) - emit('from_server', {'cmd': 'updatewidepth', 'data': vars.widepth}) - emit('from_server', {'cmd': 'updateuseprompt', 'data': vars.useprompt}) - emit('from_server', {'cmd': 'updateadventure', 'data': vars.adventure}) + emit('from_server', {'cmd': 'updateanotedepth', 'data': vars.andepth}, broadcast=True) + emit('from_server', {'cmd': 'updatewidepth', 'data': vars.widepth}, broadcast=True) + emit('from_server', {'cmd': 'updateuseprompt', 'data': vars.useprompt}, broadcast=True) + emit('from_server', {'cmd': 'updateadventure', 'data': vars.adventure}, broadcast=True) - emit('from_server', {'cmd': 'updatefrmttriminc', 'data': vars.formatoptns["frmttriminc"]}) - emit('from_server', {'cmd': 'updatefrmtrmblln', 'data': vars.formatoptns["frmtrmblln"]}) - emit('from_server', {'cmd': 'updatefrmtrmspch', 'data': vars.formatoptns["frmtrmspch"]}) - emit('from_server', {'cmd': 'updatefrmtadsnsp', 'data': vars.formatoptns["frmtadsnsp"]}) + emit('from_server', {'cmd': 'updatefrmttriminc', 'data': vars.formatoptns["frmttriminc"]}, broadcast=True) + emit('from_server', {'cmd': 'updatefrmtrmblln', 'data': vars.formatoptns["frmtrmblln"]}, broadcast=True) + emit('from_server', {'cmd': 'updatefrmtrmspch', 'data': vars.formatoptns["frmtrmspch"]}, broadcast=True) + emit('from_server', {'cmd': 'updatefrmtadsnsp', 'data': vars.formatoptns["frmtadsnsp"]}, broadcast=True) # Allow toggle events again - emit('from_server', {'cmd': 'allowtoggle', 'data': True}) + emit('from_server', {'cmd': 'allowtoggle', 'data': True}, broadcast=True) #==================================================================# # Sets the logical and display states for the AI Busy condition @@ -1227,10 +1244,10 @@ def refresh_settings(): def set_aibusy(state): if(state): vars.aibusy = True - emit('from_server', {'cmd': 'setgamestate', 'data': 'wait'}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'wait'}, broadcast=True) else: vars.aibusy = False - emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) #==================================================================# # @@ -1242,8 +1259,8 @@ def editrequest(n): txt = vars.actions[n-1] vars.editln = n - emit('from_server', {'cmd': 'setinputtext', 'data': txt}) - emit('from_server', {'cmd': 'enablesubmit', 'data': ''}) + emit('from_server', {'cmd': 'setinputtext', 'data': txt}, broadcast=True) + emit('from_server', {'cmd': 'enablesubmit', 'data': ''}, broadcast=True) #==================================================================# # @@ -1256,8 +1273,8 @@ def editsubmit(data): vars.mode = "play" refresh_story() - emit('from_server', {'cmd': 'texteffect', 'data': vars.editln}) - emit('from_server', {'cmd': 'editmode', 'data': 'false'}) + emit('from_server', {'cmd': 'texteffect', 'data': vars.editln}, broadcast=True) + emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True) #==================================================================# # @@ -1271,7 +1288,7 @@ def deleterequest(): del vars.actions[vars.editln-1] vars.mode = "play" refresh_story() - emit('from_server', {'cmd': 'editmode', 'data': 'false'}) + emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True) #==================================================================# # Toggles the game mode for memory editing and sends UI commands @@ -1279,12 +1296,12 @@ def deleterequest(): def togglememorymode(): if(vars.mode == "play"): vars.mode = "memory" - emit('from_server', {'cmd': 'memmode', 'data': 'true'}) - emit('from_server', {'cmd': 'setinputtext', 'data': vars.memory}) - emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}) + emit('from_server', {'cmd': 'memmode', 'data': 'true'}, broadcast=True) + emit('from_server', {'cmd': 'setinputtext', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) elif(vars.mode == "memory"): vars.mode = "play" - emit('from_server', {'cmd': 'memmode', 'data': 'false'}) + emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) #==================================================================# # Toggles the game mode for WI editing and sends UI commands @@ -1292,13 +1309,13 @@ def togglememorymode(): def togglewimode(): if(vars.mode == "play"): vars.mode = "wi" - emit('from_server', {'cmd': 'wimode', 'data': 'true'}) + emit('from_server', {'cmd': 'wimode', 'data': 'true'}, broadcast=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'}) + emit('from_server', {'cmd': 'wimode', 'data': 'false'}, broadcast=True) #==================================================================# # @@ -1306,7 +1323,7 @@ def togglewimode(): def addwiitem(): ob = {"key": "", "keysecondary": "", "content": "", "num": len(vars.worldinfo), "init": False, "selective": False} vars.worldinfo.append(ob); - emit('from_server', {'cmd': 'addwiitem', 'data': ob}) + emit('from_server', {'cmd': 'addwiitem', 'data': ob}, broadcast=True) #==================================================================# # @@ -1316,7 +1333,7 @@ def sendwi(): ln = len(vars.worldinfo) # Clear contents of WI container - emit('from_server', {'cmd': 'clearwi', 'data': ''}) + emit('from_server', {'cmd': 'clearwi', 'data': ''}, broadcast=True) # If there are no WI entries, send an empty WI object if(ln == 0): @@ -1325,7 +1342,7 @@ def sendwi(): # Send contents of WI array for wi in vars.worldinfo: ob = wi - emit('from_server', {'cmd': 'addwiitem', 'data': ob}) + emit('from_server', {'cmd': 'addwiitem', 'data': ob}, broadcast=True) # Make sure last WI item is uninitialized if(vars.worldinfo[-1]["init"]): addwiitem() @@ -1337,7 +1354,7 @@ def requestwi(): list = [] for wi in vars.worldinfo: list.append(wi["num"]) - emit('from_server', {'cmd': 'requestwiitem', 'data': list}) + emit('from_server', {'cmd': 'requestwiitem', 'data': list}, broadcast=True) #==================================================================# # Renumber WI items consecutively @@ -1445,10 +1462,10 @@ def memsubmit(data): # For now just send it to storage vars.memory = data vars.mode = "play" - emit('from_server', {'cmd': 'memmode', 'data': 'false'}) + emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) # Ask for contents of Author's Note field - emit('from_server', {'cmd': 'getanote', 'data': ''}) + emit('from_server', {'cmd': 'getanote', 'data': ''}, broadcast=True) #==================================================================# # Commit changes to Author's Note @@ -1494,7 +1511,7 @@ def ikrequest(txt): print("{0}{1}{2}".format(colors.CYAN, genout, colors.END)) vars.actions.append(genout) refresh_story() - emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) + emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}, broadcast=True) set_aibusy(0) else: @@ -1506,7 +1523,7 @@ def ikrequest(txt): code = er["errors"][0]["extensions"]["code"] errmsg = "InferKit API Error: {0} - {1}".format(req.status_code, code) - emit('from_server', {'cmd': 'errmsg', 'data': errmsg}) + emit('from_server', {'cmd': 'errmsg', 'data': errmsg}, broadcast=True) set_aibusy(0) #==================================================================# @@ -1544,7 +1561,7 @@ def oairequest(txt, min, max): print("{0}{1}{2}".format(colors.CYAN, genout, colors.END)) vars.actions.append(genout) refresh_story() - emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}) + emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)}, broadcast=True) set_aibusy(0) else: @@ -1555,7 +1572,7 @@ def oairequest(txt, min, max): message = er["error"]["message"] errmsg = "OpenAI API Error: {0} - {1}".format(type, message) - emit('from_server', {'cmd': 'errmsg', 'data': errmsg}) + emit('from_server', {'cmd': 'errmsg', 'data': errmsg}, broadcast=True) set_aibusy(0) #==================================================================# @@ -1563,11 +1580,11 @@ def oairequest(txt, min, max): #==================================================================# def exitModes(): if(vars.mode == "edit"): - emit('from_server', {'cmd': 'editmode', 'data': 'false'}) + emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True) elif(vars.mode == "memory"): - emit('from_server', {'cmd': 'memmode', 'data': 'false'}) + emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) elif(vars.mode == "wi"): - emit('from_server', {'cmd': 'wimode', 'data': 'false'}) + emit('from_server', {'cmd': 'wimode', 'data': 'false'}, broadcast=True) vars.mode = "play" #==================================================================# @@ -1708,8 +1725,8 @@ def loadRequest(loadpath): # Refresh game screen sendwi() refresh_story() - emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) - emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) + emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True) print("{0}Story loaded from {1}!{2}".format(colors.GREEN, path.basename(loadpath), colors.END)) #==================================================================# @@ -1731,7 +1748,7 @@ def importRequest(): vars.importjs = vars.importjs["stories"] # Clear Popup Contents - emit('from_server', {'cmd': 'clearpopup', 'data': ''}) + emit('from_server', {'cmd': 'clearpopup', 'data': ''}, broadcast=True) # Initialize vars num = 0 @@ -1824,8 +1841,8 @@ def importgame(): # Refresh game screen sendwi() refresh_story() - emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) - emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) + emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True) #==================================================================# # Import an aidg.club prompt and start a new game with it. @@ -1867,7 +1884,7 @@ def importAidgRequest(id): # Refresh game screen sendwi() refresh_story() - emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}) + emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) #==================================================================# # Import World Info JSON file From b1c13f832ae11e7b614dcdc0372e359370e0e8d5 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 10:25:03 -0400 Subject: [PATCH 021/117] Implement arrmansa's low VRAM patch --- aiserver.py | 151 +++++++++++++--- breakmodel.py | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 610 insertions(+), 28 deletions(-) create mode 100644 breakmodel.py diff --git a/aiserver.py b/aiserver.py index 2756d67c..263b863b 100644 --- a/aiserver.py +++ b/aiserver.py @@ -13,12 +13,15 @@ import json import requests import html import argparse +import sys +import gc # KoboldAI import fileops import gensettings from utils import debounce import utils +import breakmodel #==================================================================# # Variables & Storage @@ -100,6 +103,8 @@ class vars: saveow = False # Whether or not overwrite confirm has been displayed genseqs = [] # Temporary storage for generated sequences useprompt = True # Whether to send the full prompt with every submit action + breakmodel = False # For GPU users, whether to use both system RAM and VRAM to conserve VRAM while offering speedup compared to CPU-only + bmsupported = False # Whether the breakmodel option is supported (GPT-Neo/GPT-J only, currently) acregex_ai = re.compile(r'\n* *>(.|\n)*') # Pattern for matching adventure actions from the AI so we can remove them acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses) actionmode = 1 @@ -160,6 +165,8 @@ parser.add_argument("--remote", action='store_true', help="Optimizes KoboldAI fo parser.add_argument("--model", help="Specify the Model Type to skip the Menu") parser.add_argument("--path", help="Specify the Path for local models (For model NeoCustom or GPT2Custom)") parser.add_argument("--cpu", action='store_true', help="By default unattended launches are on the GPU use this option to force CPU usage.") +parser.add_argument("--breakmodel", action='store_true', help="For models that support GPU-CPU hybrid generation, use this feature instead of GPU or CPU generation") +parser.add_argument("--breakmodel_layers", type=int, help="Specify the number of layers to commit to system RAM if --breakmodel is used") args = parser.parse_args() vars.model = args.model; @@ -184,6 +191,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): import torch print("{0}Looking for GPU support...{1}".format(colors.PURPLE, colors.END), end="") vars.hascuda = torch.cuda.is_available() + vars.bmsupported = vars.model in ("EleutherAI/gpt-neo-1.3B", "EleutherAI/gpt-neo-2.7B", "NeoCustom") if(vars.hascuda): print("{0}FOUND!{1}".format(colors.GREEN, colors.END)) else: @@ -193,23 +201,40 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): if(vars.hascuda): genselected = True vars.usegpu = True + vars.breakmodel = False if(args.cpu): vars.usegpu = False - elif(vars.hascuda): - print("{0}Use GPU or CPU for generation?: (Default GPU){1}\n".format(colors.CYAN, colors.END)) - print(" 1 - GPU\n 2 - CPU\n") + vars.breakmodel = False + if(args.breakmodel): + vars.usegpu = False + vars.breakmodel = True + elif(vars.hascuda): + if(vars.bmsupported): + print(colors.YELLOW + "You're using a model that supports GPU-CPU hybrid generation!\nCurrently only GPT-Neo models and GPT-J-6B support this feature.") + print("{0}Use GPU or CPU for generation?: (Default GPU){1}".format(colors.CYAN, colors.END)) + if(vars.bmsupported): + print(f" 1 - GPU\n 2 - CPU\n 3 - Both (slower than GPU-only but uses less VRAM)\n") + else: + print(" 1 - GPU\n 2 - CPU\n") genselected = False if(vars.hascuda): while(genselected == False): genselect = input("Mode> ") if(genselect == ""): + vars.breakmodel = False vars.usegpu = True genselected = True elif(genselect.isnumeric() and int(genselect) == 1): + vars.breakmodel = False vars.usegpu = True genselected = True elif(genselect.isnumeric() and int(genselect) == 2): + vars.breakmodel = False + vars.usegpu = False + genselected = True + elif(vars.bmsupported and genselect.isnumeric() and int(genselect) == 3): + vars.breakmodel = True vars.usegpu = False genselected = True else: @@ -343,15 +368,45 @@ print("{0}OK!{1}".format(colors.GREEN, colors.END)) if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): if(not vars.noai): print("{0}Initializing transformers, please wait...{1}".format(colors.PURPLE, colors.END)) - from transformers import pipeline, GPT2Tokenizer, GPT2LMHeadModel, GPTNeoForCausalLM + from transformers import pipeline, GPT2Tokenizer, GPT2LMHeadModel, GPTNeoForCausalLM, GPTNeoModel, AutoModel # If custom GPT Neo model was chosen if(vars.model == "NeoCustom"): model = GPTNeoForCausalLM.from_pretrained(vars.custmodpth) tokenizer = GPT2Tokenizer.from_pretrained(vars.custmodpth) # Is CUDA available? If so, use GPU, otherwise fall back to CPU - if(vars.hascuda and vars.usegpu): - generator = pipeline('text-generation', model=model, tokenizer=tokenizer, device=0) + if(vars.hascuda): + if(vars.usegpu): + generator = pipeline('text-generation', model=model, tokenizer=tokenizer, device=0) + elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel) + n_layers = model.config.num_layers + breakmodel.total_blocks = n_layers + model.half().to('cpu') + gc.collect() + model.lm_head.to(breakmodel.gpu_device) + model.transformer.wte.to(breakmodel.gpu_device) + model.transformer.ln_f.to(breakmodel.gpu_device) + gc.collect() + if(args.breakmodel): + breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) + else: + print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") + print("The more of them you put into system RAM, the slower it will run,") + print("but it will require less VRAM") + print("(roughly proportional to number of layers).") + print(f"This model has{colors.YELLOW} {n_layers} {colors.CYAN}layers.{colors.END}\n") + while(True): + layerselect = input("# of layers> ") + if(layerselect.isnumeric() and 0 <= int(layerselect) <= n_layers): + breakmodel.ram_blocks = int(layerselect) + break + else: + print(f"{colors.RED}Please enter an integer between 0 and {n_layers}.{colors.END}") + print(f"{colors.PURPLE}Will commit{colors.YELLOW} {breakmodel.ram_blocks} {colors.PURPLE}of{colors.YELLOW} {n_layers} {colors.PURPLE}layers to system RAM.{colors.END}") + GPTNeoModel.forward = breakmodel.new_forward + generator = model.generate + else: + generator = pipeline('text-generation', model=model, tokenizer=tokenizer) else: generator = pipeline('text-generation', model=model, tokenizer=tokenizer) # If custom GPT2 model was chosen @@ -367,8 +422,39 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): else: # Is CUDA available? If so, use GPU, otherwise fall back to CPU tokenizer = GPT2Tokenizer.from_pretrained(vars.model) - if(vars.hascuda and vars.usegpu): - generator = pipeline('text-generation', model=vars.model, device=0) + if(vars.hascuda): + if(vars.usegpu): + generator = pipeline('text-generation', model=vars.model, device=0) + elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel) + model = AutoModel.from_pretrained(vars.model) + n_layers = model.config.num_layers + breakmodel.total_blocks = n_layers + model.half().to('cpu') + gc.collect() + model.lm_head.to(breakmodel.gpu_device) + model.transformer.wte.to(breakmodel.gpu_device) + model.transformer.ln_f.to(breakmodel.gpu_device) + gc.collect() + if(args.breakmodel): + breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) + else: + print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") + print("The more of them you put into system RAM, the slower it will run,") + print("but it will require less VRAM") + print("(roughly proportional to number of layers).") + print(f"This model has{colors.YELLOW} {n_layers} {colors.CYAN}layers.{colors.END}\n") + while(True): + layerselect = input("# of layers> ") + if(layerselect.isnumeric() and 0 <= int(layerselect) <= n_layers): + breakmodel.ram_blocks = int(layerselect) + break + else: + print(f"{colors.RED}Please enter an integer between 0 and {n_layers}.{colors.END}") + print(f"{colors.PURPLE}Will commit{colors.YELLOW} {breakmodel.ram_blocks} {colors.PURPLE}of{colors.YELLOW} {n_layers} {colors.PURPLE}layers to system RAM.{colors.END}") + GPTNeoModel.forward = breakmodel.new_forward + generator = model.generate + else: + generator = pipeline('text-generation', model=vars.model) else: generator = pipeline('text-generation', model=vars.model) @@ -480,42 +566,42 @@ def get_message(msg): elif(msg['cmd'] == 'settemp'): vars.temp = float(msg['data']) emit('from_server', {'cmd': 'setlabeltemp', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'settopp'): vars.top_p = float(msg['data']) emit('from_server', {'cmd': 'setlabeltopp', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'settopk'): vars.top_k = int(msg['data']) emit('from_server', {'cmd': 'setlabeltopk', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'settfs'): vars.tfs = float(msg['data']) emit('from_server', {'cmd': 'setlabeltfs', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setreppen'): vars.rep_pen = float(msg['data']) emit('from_server', {'cmd': 'setlabelreppen', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setoutput'): vars.genamt = int(msg['data']) emit('from_server', {'cmd': 'setlabeloutput', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'settknmax'): vars.max_length = int(msg['data']) emit('from_server', {'cmd': 'setlabeltknmax', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setikgen'): vars.ikgen = int(msg['data']) emit('from_server', {'cmd': 'setlabelikgen', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() # Author's Note field update elif(msg['cmd'] == 'anote'): @@ -524,28 +610,28 @@ def get_message(msg): elif(msg['cmd'] == 'anotedepth'): vars.andepth = int(msg['data']) emit('from_server', {'cmd': 'setlabelanotedepth', 'data': msg['data']}, broadcast=True) - settingschanged() + settingschanged() refresh_settings() # Format - Trim incomplete sentences elif(msg['cmd'] == 'frmttriminc'): if('frmttriminc' in vars.formatoptns): vars.formatoptns["frmttriminc"] = msg['data'] - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'frmtrmblln'): if('frmtrmblln' in vars.formatoptns): vars.formatoptns["frmtrmblln"] = msg['data'] - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'frmtrmspch'): if('frmtrmspch' in vars.formatoptns): vars.formatoptns["frmtrmspch"] = msg['data'] - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'frmtadsnsp'): if('frmtadsnsp' in vars.formatoptns): vars.formatoptns["frmtadsnsp"] = msg['data'] - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'importselect'): vars.importnum = int(msg["data"].replace("import", "")) @@ -589,20 +675,20 @@ def get_message(msg): elif(msg['cmd'] == 'setnumseq'): vars.numseqs = int(msg['data']) emit('from_server', {'cmd': 'setlabelnumseq', 'data': msg['data']}) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setwidepth'): vars.widepth = int(msg['data']) emit('from_server', {'cmd': 'setlabelwidepth', 'data': msg['data']}) - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setuseprompt'): vars.useprompt = msg['data'] - settingschanged() + settingschanged() refresh_settings() elif(msg['cmd'] == 'setadventure'): vars.adventure = msg['data'] - settingschanged() + settingschanged() refresh_settings() refresh_story() elif(msg['cmd'] == 'importwi'): @@ -984,7 +1070,8 @@ def generate(txt, min, max): vars.lastctx = txt # Clear CUDA cache if using GPU - if(vars.hascuda and vars.usegpu): + if(vars.hascuda and (vars.usegpu or vars.breakmodel)): + gc.collect() torch.cuda.empty_cache() # Submit input text to generator @@ -992,9 +1079,17 @@ def generate(txt, min, max): top_p = vars.top_p if vars.top_p > 0.0 else None top_k = vars.top_k if vars.top_k > 0 else None tfs = vars.tfs if vars.tfs > 0.0 else None + + # generator() only accepts a torch tensor of tokens (long datatype) as + # its first argument if we're using breakmodel, otherwise a string + # is fine + if(vars.hascuda and vars.breakmodel): + gen_in = tokenizer.encode(txt, return_tensors="pt", truncation=True).long().to(breakmodel.gpu_device) + else: + gen_in = txt genout = generator( - txt, + gen_in, do_sample=True, min_length=min, max_length=max, @@ -1965,4 +2060,4 @@ if __name__ == "__main__": else: import webbrowser webbrowser.open_new('http://localhost:5000') - socketio.run(app) \ No newline at end of file + socketio.run(app) diff --git a/breakmodel.py b/breakmodel.py new file mode 100644 index 00000000..163681ad --- /dev/null +++ b/breakmodel.py @@ -0,0 +1,487 @@ +''' +This is a MODIFIED version of arrmansa's low VRAM patch. +https://github.com/arrmansa/Basic-UI-for-GPT-J-6B-with-low-vram/blob/main/GPT-J-6B-Low-Vram-UI.ipynb +Copyright 2021 arrmansa +Copyright 2021 finetuneanon +Copyright 2018 The Hugging Face team +Released under the Apache License 2.0 + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +''' + + +import torch +import copy +import gc + +from transformers.modeling_outputs import BaseModelOutputWithPast + +from transformers.utils import logging +logger = logging.get_logger(__name__) + + +class MaxSharedRamBlocksException(Exception): + def __init__(self, i: int): + self.corrected_max_shared_ram_blocks = i + super().__init__('max_shared_ram_blocks is set too high, please set it to '+str(i)) + + +breakmodel = True +gpu_device = 'cuda' +total_blocks = 24 +ram_blocks = 7 +max_shared_ram_blocks = None + + +def new_forward( + self, + input_ids=None, + past_key_values=None, + attention_mask=None, + token_type_ids=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + embs=None, + ): + global max_shared_ram_blocks + + if breakmodel: + if max_shared_ram_blocks is None: + max_shared_ram_blocks = total_blocks + + if not hasattr(self, 'extrastorage'): + setattr(self,"extrastorage",{}) + torch.cuda.empty_cache() + + for i in range(ram_blocks,len(self.h)): + self.h[i].to(gpu_device) + + for i in range(ram_blocks): + self.h[i].to("cpu") + self.extrastorage[i] = copy.deepcopy(self.h[i]) + smalltensor = torch.tensor(0).to(gpu_device) + for param1 in self.h[i].parameters(): + param1.data = smalltensor + self.h[i].to(gpu_device) + + for i in range(len(self.h)): + for param in self.h[i].parameters(): + param.requires_grad = False + param.data = param.data.detach() + gc.collect() + torch.cuda.empty_cache() + + for i in range(ram_blocks): + for param in self.extrastorage[i].parameters(): + param.requires_grad = False + if i < max_shared_ram_blocks: + try: + param.data = param.data.detach().pin_memory() + except: + raise MaxSharedRamBlocksException(i) + else: + param.data = param.data.detach() + gc.collect() + torch.cuda.empty_cache() + + for param1,param2 in zip(self.h[0].parameters(),self.extrastorage[0].parameters()): + param1.data = param2.data.to(gpu_device, non_blocking=False).detach() + + for param1,param2 in zip(self.h[ram_blocks-1].parameters(),self.extrastorage[ram_blocks-1].parameters()): + param1.data = param2.data.to(gpu_device, non_blocking=False).detach() + #END MODEL BREAK EDITS + + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + use_cache = use_cache if use_cache is not None else self.config.use_cache + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + input_ids = input_ids.view(-1, input_shape[-1]) + batch_size = input_ids.shape[0] + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size = inputs_embeds.shape[0] + else: + raise ValueError("You have to specify either input_ids or inputs_embeds") + + device = input_ids.device if input_ids is not None else inputs_embeds.device + + if token_type_ids is not None: + token_type_ids = token_type_ids.view(-1, input_shape[-1]) + if position_ids is not None: + position_ids = position_ids.view(-1, input_shape[-1]) + + if past_key_values is None: + past_length = 0 + past_key_values = tuple([None] * len(self.h)) + else: + past_length = past_key_values[0][0].size(-2) + + device = input_ids.device if input_ids is not None else inputs_embeds.device + if position_ids is None: + position_ids = torch.arange(past_length, input_shape[-1] + past_length, dtype=torch.long, device=device) + position_ids = position_ids.unsqueeze(0).view(-1, input_shape[-1]) + + # Attention mask. + if attention_mask is not None: + assert batch_size > 0, "batch_size has to be defined and > 0" + global_attention_mask = attention_mask.view(batch_size, -1) + # We create a 3D attention mask from a 2D tensor mask. + # Sizes are [batch_size, 1, 1, to_seq_length] + # So we can broadcast to [batch_size, num_heads, from_seq_length, to_seq_length] + # this attention mask is more simple than the triangular masking of causal attention + # used in OpenAI GPT, we just need to prepare the broadcast dimension here. + global_attention_mask = global_attention_mask[:, None, None, :] + + # Since global_attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + global_attention_mask = global_attention_mask.to(dtype=self.dtype) # fp16 compatibility + global_attention_mask = (1.0 - global_attention_mask) * -10000.0 + else: + global_attention_mask = None + + # Local causal attention mask + batch_size, seq_length = input_shape + full_seq_length = seq_length + past_length + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x num_heads x N x N + # head_mask has shape n_layer x batch x num_heads x N x N + head_mask = self.get_head_mask(head_mask, self.config.num_layers) + + if inputs_embeds is None: + inputs_embeds = self.wte(input_ids) + + if embs is not None and not (use_cache is not None and use_cache and past_key_values is not None and len(past_key_values) > 0 and past_key_values[0] is not None): + offset = 0 + for pos, emb in embs: + pos += offset + if len(emb.shape) == 2: + emb = emb.repeat(input_shape[0], 1, 1) + inputs_embeds[:, pos:pos+emb.shape[1]] = emb + offset += emb.shape[1] + + if self.rotary: + hidden_states = inputs_embeds + else: + position_embeds = self.wpe(position_ids) + hidden_states = inputs_embeds + position_embeds + + if token_type_ids is not None: + token_type_embeds = self.wte(token_type_ids) + hidden_states = hidden_states + token_type_embeds + + hidden_states = self.drop(hidden_states) + + output_shape = input_shape + (hidden_states.size(-1),) + + presents = () if use_cache else None + all_self_attentions = () if output_attentions else None + all_hidden_states = () if output_hidden_states else None + + + if breakmodel: + copystream = torch.cuda.Stream(device=0,priority = -1) + + for i, (block, layer_past) in enumerate(zip(self.h, past_key_values)): + + if breakmodel: + if i in range(ram_blocks): + index1 = (i+1)%ram_blocks + for param1,param2 in zip(self.h[index1].parameters(),self.h[(i-1)%ram_blocks].parameters()): + param1.data = param2.data + for param1,param2 in zip(self.h[index1].parameters(),self.extrastorage[index1].parameters()): + with torch.cuda.stream(copystream): + torch.cuda.comm.broadcast(param2.data,out = [param1.data]) + + + attn_type = self.config.attention_layers[i] + attn_mask = global_attention_mask + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states.cpu(),) + + if getattr(self.config, "gradient_checkpointing", False) and self.training: + + if use_cache: + logger.warning( + "`use_cache=True` is incompatible with `config.gradient_checkpointing=True`. Setting " + "`use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + # None for past_key_value + return module(*inputs, use_cache, output_attentions) + + return custom_forward + + outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states, + None, + attn_mask, + head_mask[i], + ) + else: + outputs = block( + hidden_states, + layer_past=layer_past, + attention_mask=attn_mask, + head_mask=head_mask[i], + use_cache=use_cache, + output_attentions=output_attentions, + ) + + hidden_states = outputs[0] + if use_cache is True: + presents = presents + (outputs[1],) + + if output_attentions: + all_self_attentions = all_self_attentions + (outputs[2 if use_cache else 1],) + + + if breakmodel: + if i in range(ram_blocks): + torch.cuda.synchronize() + torch.cuda.empty_cache() + + if breakmodel: + del copystream + + torch.cuda.empty_cache() + + + hidden_states = self.ln_f(hidden_states) + + hidden_states = hidden_states.view(*output_shape) + # Add last hidden state + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple(v for v in [hidden_states, presents, all_hidden_states, all_self_attentions] if v is not None) + + return BaseModelOutputWithPast( + last_hidden_state=hidden_states, + past_key_values=presents, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + ) From eef0db8deec1735c5b8eabe6e786596101b6a1b3 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 10:47:54 -0400 Subject: [PATCH 022/117] Specifically import torch.cuda.comm in breakmodel.py --- breakmodel.py | 1 + 1 file changed, 1 insertion(+) diff --git a/breakmodel.py b/breakmodel.py index 163681ad..7417db36 100644 --- a/breakmodel.py +++ b/breakmodel.py @@ -212,6 +212,7 @@ Released under the Apache License 2.0 import torch +import torch.cuda.comm import copy import gc From 7717168676cd8866307ee574a42c69a691c8ecad Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 10:52:57 -0400 Subject: [PATCH 023/117] Only allow --breakmodel if it's supported --- aiserver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aiserver.py b/aiserver.py index 263b863b..a8f65445 100644 --- a/aiserver.py +++ b/aiserver.py @@ -205,7 +205,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): if(args.cpu): vars.usegpu = False vars.breakmodel = False - if(args.breakmodel): + if(vars.bmsupported and args.breakmodel): vars.usegpu = False vars.breakmodel = True elif(vars.hascuda): @@ -387,7 +387,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) gc.collect() - if(args.breakmodel): + if(vars.bmsupported and args.breakmodel): breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) else: print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") @@ -435,7 +435,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) gc.collect() - if(args.breakmodel): + if(vars.bmsupported and args.breakmodel): breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) else: print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") From f986c65a4eed8540ddac7401ad612f81fcc3f1db Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 11:15:32 -0400 Subject: [PATCH 024/117] Manually strip and decode tokens if not using a pipeline --- aiserver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aiserver.py b/aiserver.py index a8f65445..40e7c602 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1109,6 +1109,10 @@ def generate(txt, min, max): set_aibusy(0) return + # Need to manually strip and decode tokens if we're not using a pipeline + if(vars.hascuda and vars.breakmodel): + genout = [{"generated_text": tokenizer.decode(tokens[len(gen_in[0])-len(tokens):])} for tokens in genout] + if(len(genout) == 1): genresult(genout[0]["generated_text"]) else: From 5f82e5ba0dc920784c7f4e90dbb3eed2153d8e06 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 11:17:34 -0400 Subject: [PATCH 025/117] Also clear CUDA cache twice if using breakmodel --- aiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index 40e7c602..45bd28ed 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1119,7 +1119,7 @@ def generate(txt, min, max): genselect(genout) # Clear CUDA cache again if using GPU - if(vars.hascuda and vars.usegpu): + if(vars.hascuda and (vars.usegpu or vars.breakmodel)): torch.cuda.empty_cache() set_aibusy(0) From 56c9dc2c04711e64cca22ff434d3369b2a2b0776 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 11:34:31 -0400 Subject: [PATCH 026/117] Fix "Expected all tensors to" on non-rotary models Expected all tensors to be on the same device, but found at least two devices, cpu and cuda:0! (when checking arugment for argument index in method wrapper_index_select) --- aiserver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aiserver.py b/aiserver.py index 45bd28ed..660dc7cc 100644 --- a/aiserver.py +++ b/aiserver.py @@ -386,6 +386,8 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) + if(not model.config.rotary): + model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() if(vars.bmsupported and args.breakmodel): breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) @@ -434,6 +436,8 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) + if(not model.config.rotary): + model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() if(vars.bmsupported and args.breakmodel): breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) From 68836728d42a82768d4c8b635697bcf59e626b98 Mon Sep 17 00:00:00 2001 From: henk717 Date: Fri, 20 Aug 2021 17:51:49 +0200 Subject: [PATCH 027/117] Update World Info on Submit Still VERY far from ideal for multiplayer, only one person can realistically edit it at a time. Whoever submits counts. Will need more major interface changes so things can be submitted one by one. But hey, it works and its good enough for a group of friends to play the game :D --- aiserver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aiserver.py b/aiserver.py index 2756d67c..8fbac449 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1316,6 +1316,7 @@ def togglewimode(): # Then set UI state back to Play vars.mode = "play" emit('from_server', {'cmd': 'wimode', 'data': 'false'}, broadcast=True) + sendwi() #==================================================================# # From 8bfcf86a8b83d24a833f2b1de364980ad5ade589 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 13:00:53 -0400 Subject: [PATCH 028/117] Fix for non-rotary models without "rotary" in config.json --- aiserver.py | 4 ++-- breakmodel.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aiserver.py b/aiserver.py index 660dc7cc..c0d784eb 100644 --- a/aiserver.py +++ b/aiserver.py @@ -386,7 +386,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) - if(not model.config.rotary): + if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() if(vars.bmsupported and args.breakmodel): @@ -436,7 +436,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) - if(not model.config.rotary): + if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() if(vars.bmsupported and args.breakmodel): diff --git a/breakmodel.py b/breakmodel.py index 7417db36..8154b623 100644 --- a/breakmodel.py +++ b/breakmodel.py @@ -378,7 +378,7 @@ def new_forward( inputs_embeds[:, pos:pos+emb.shape[1]] = emb offset += emb.shape[1] - if self.rotary: + if hasattr(self, 'rotary') and self.rotary: hidden_states = inputs_embeds else: position_embeds = self.wpe(position_ids) From e00d9c43627a6f98724f252ca77902ae7be99dbf Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 19:32:18 -0400 Subject: [PATCH 029/117] breakmodel fix for models without lm_head --- aiserver.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index c0d784eb..ea1896af 100644 --- a/aiserver.py +++ b/aiserver.py @@ -383,9 +383,10 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): breakmodel.total_blocks = n_layers model.half().to('cpu') gc.collect() - model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) + if(hasattr(model), 'lm_head'): + model.lm_head.to(breakmodel.gpu_device) if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() @@ -433,9 +434,10 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): breakmodel.total_blocks = n_layers model.half().to('cpu') gc.collect() - model.lm_head.to(breakmodel.gpu_device) model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) + if(hasattr(model), 'lm_head'): + model.lm_head.to(breakmodel.gpu_device) if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() From a8bbfab87a06f0f8fb6266db02963dd64ce76861 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Fri, 20 Aug 2021 20:50:03 -0400 Subject: [PATCH 030/117] Actually use args.breakmodel_layers --- aiserver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aiserver.py b/aiserver.py index ea1896af..b9604a00 100644 --- a/aiserver.py +++ b/aiserver.py @@ -390,8 +390,8 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() - if(vars.bmsupported and args.breakmodel): - breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) + if(args.breakmodel_layers is not None): + breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel_layers)) else: print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") print("The more of them you put into system RAM, the slower it will run,") @@ -441,8 +441,8 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) gc.collect() - if(vars.bmsupported and args.breakmodel): - breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel)) + if(args.breakmodel_layers is not None): + breakmodel.ram_blocks = max(0, min(n_layers, args.breakmodel_layers)) else: print(colors.CYAN + "\nHow many layers would you like to put into system RAM?") print("The more of them you put into system RAM, the slower it will run,") From fae15b8a17a0edc3bb71f953142007334f8b991e Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sat, 21 Aug 2021 10:54:57 -0400 Subject: [PATCH 031/117] Fix typo in previous commit --- aiserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index b9604a00..b3ac201a 100644 --- a/aiserver.py +++ b/aiserver.py @@ -385,7 +385,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): gc.collect() model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) - if(hasattr(model), 'lm_head'): + if(hasattr(model, 'lm_head')): model.lm_head.to(breakmodel.gpu_device) if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) @@ -436,7 +436,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]): gc.collect() model.transformer.wte.to(breakmodel.gpu_device) model.transformer.ln_f.to(breakmodel.gpu_device) - if(hasattr(model), 'lm_head'): + if(hasattr(model, 'lm_head')): model.lm_head.to(breakmodel.gpu_device) if(not hasattr(model.config, 'rotary') or not model.config.rotary): model.transformer.wpe.to(breakmodel.gpu_device) From 3c9ce2c541c66310f61d7eedeb42937ef4c08582 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sat, 21 Aug 2021 12:15:31 -0400 Subject: [PATCH 032/117] Use torch.no_grad() and more garbage collection --- aiserver.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/aiserver.py b/aiserver.py index b3ac201a..a88657b9 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1094,21 +1094,22 @@ def generate(txt, min, max): else: gen_in = txt - genout = generator( - gen_in, - do_sample=True, - min_length=min, - max_length=max, - repetition_penalty=vars.rep_pen, - top_p=top_p, - top_k=top_k, - tfs=tfs, - temperature=vars.temp, - bad_words_ids=vars.badwordsids, - use_cache=True, - return_full_text=False, - num_return_sequences=vars.numseqs - ) + with torch.no_grad(): + genout = generator( + gen_in, + do_sample=True, + min_length=min, + max_length=max, + repetition_penalty=vars.rep_pen, + top_p=top_p, + top_k=top_k, + tfs=tfs, + temperature=vars.temp, + bad_words_ids=vars.badwordsids, + use_cache=True, + return_full_text=False, + num_return_sequences=vars.numseqs + ) except Exception as e: emit('from_server', {'cmd': 'errmsg', 'data': 'Error occured during generator call, please check console.'}, broadcast=True) print("{0}{1}{2}".format(colors.RED, e, colors.END)) @@ -1126,6 +1127,8 @@ def generate(txt, min, max): # Clear CUDA cache again if using GPU if(vars.hascuda and (vars.usegpu or vars.breakmodel)): + del genout + gc.collect() torch.cuda.empty_cache() set_aibusy(0) From a151e1a33a9e7111b0595f0a57a2251f57d91541 Mon Sep 17 00:00:00 2001 From: henk717 Date: Sun, 22 Aug 2021 15:54:35 +0200 Subject: [PATCH 033/117] Small fix for Authors Notes in multiplayer Multiplayer support was causing all players to automatically submit authors notes. This is now fixed only the person submitting the authors notes counts. --- aiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index ef66f82d..e1b6e2c7 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1406,7 +1406,7 @@ def togglememorymode(): vars.mode = "memory" emit('from_server', {'cmd': 'memmode', 'data': 'true'}, broadcast=True) emit('from_server', {'cmd': 'setinputtext', 'data': vars.memory}, broadcast=True) - emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}) elif(vars.mode == "memory"): vars.mode = "play" emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) From 2fd544cad71353fd00f0236c6a6524c33420f6f1 Mon Sep 17 00:00:00 2001 From: henk717 Date: Sun, 22 Aug 2021 20:59:47 +0200 Subject: [PATCH 034/117] Change max tokens to 4096 It works smoothly on the TPU colab, so lets allow it. People should not turn this all the way up unless they got the hardware, but we want to allow this for those that do. --- gensettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gensettings.py b/gensettings.py index ac531ba2..e113b8ee 100644 --- a/gensettings.py +++ b/gensettings.py @@ -70,7 +70,7 @@ gensettingstf = [{ "label": "Max Tokens", "id": "settknmax", "min": 512, - "max": 2048, + "max": 4096, "step": 8, "default": 1024, "tooltip": "Max number of tokens of context to submit to the AI for sampling. Make sure this is higher than Amount to Generate. Higher values increase VRAM/RAM usage." From 67521d53f960f6fe2f16462294ceb36e6ec50f23 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 23 Aug 2021 02:34:27 -0400 Subject: [PATCH 035/117] You can now click on story chunks to edit them --- static/application.js | 164 +++++++++++++++++++++++++++++------------- static/custom.css | 61 ++++++++++++++-- templates/index.html | 30 ++++---- 3 files changed, 189 insertions(+), 66 deletions(-) diff --git a/static/application.js b/static/application.js index 3963ed8a..ae75245c 100644 --- a/static/application.js +++ b/static/application.js @@ -21,7 +21,6 @@ var button_format; var button_mode; var button_mode_label; var button_send; -var button_actedit; var button_actmem; var button_actback; var button_actretry; @@ -67,6 +66,11 @@ var seqselcontents; var memorymode = false; var gamestarted = false; +var editmode = false; +var connected = false; +var newly_loaded = true; +var current_chunk_changed = false; +var chunk_conflict = false; // Key states var shift_down = false; @@ -75,6 +79,7 @@ var do_clear_ent = false; // Display vars var allowtoggle = false; var formatcount = 0; +var allowedit = true; // Whether clicking on chunks will edit them // Adventure var action_mode = 0; // 0: story, 1: action @@ -384,32 +389,11 @@ function popupShow(state) { } function enterEditMode() { - // Add class to each story chunk - showMessage("Please select a story chunk to edit above."); - button_actedit.html("Cancel"); - game_text.children('chunk').addClass("chunkhov"); - game_text.on('click', '> *', function() { - editModeSelect($(this).attr("n")); - }); - disableSendBtn(); - hide([button_actback, button_actmem, button_actretry, button_actwi]); - show([button_delete]); + editmode = true; } function exitEditMode() { - // Remove class to each story chunk - hideMessage(); - button_actedit.html("Edit"); - game_text.children('chunk').removeClass("chunkhov"); - game_text.off('click', '> *'); - enableSendBtn(); - show([button_actback, button_actmem, button_actretry, button_actwi]); - hide([button_delete]); - input_text.val(""); -} - -function editModeSelect(n) { - socket.send({'cmd': 'editline', 'data': n}); + editmode = false; } function enterMemoryMode() { @@ -417,7 +401,7 @@ function enterMemoryMode() { setmodevisibility(false); 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, button_actwi]); + hide([button_actback, button_actretry, button_delete, button_actwi]); // Display Author's Note field anote_menu.slideDown("fast"); } @@ -427,7 +411,7 @@ function exitMemoryMode() { setmodevisibility(adventure); hideMessage(); button_actmem.html("Memory"); - show([button_actback, button_actretry, button_actedit, button_actwi]); + show([button_actback, button_actretry, button_actwi]); input_text.val(""); // Hide Author's Note field anote_menu.slideUp("fast"); @@ -436,7 +420,7 @@ function exitMemoryMode() { 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]); + hide([button_actback, button_actmem, button_actretry, game_text]); show([wi_menu]); disableSendBtn(); } @@ -445,7 +429,7 @@ function exitWiMode() { hideMessage(); button_actwi.html("W Info"); hide([wi_menu]); - show([button_actedit, button_actback, button_actmem, button_actretry, game_text]); + show([button_actback, button_actmem, button_actretry, game_text]); enableSendBtn(); } @@ -486,10 +470,10 @@ function changemode() { } function newTextHighlight(ref) { - ref.addClass("color_green"); - ref.addClass("colorfade"); + ref.addClass("edit-flash"); setTimeout(function () { - ref.removeClass("color_green"); + ref.addClass("colorfade"); + ref.removeClass("edit-flash"); setTimeout(function () { ref.removeClass("colorfade"); }, 1000); @@ -588,12 +572,11 @@ function hideRandomStoryPopup() { function setStartState() { enableSendBtn(); enableButtons([button_actmem, button_actwi]); - disableButtons([button_actedit, button_actback, button_actretry]); + disableButtons([button_actback, button_actretry]); hide([wi_menu, button_delete]); - show([game_text, button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + show([game_text, button_actmem, button_actwi, button_actback, button_actretry]); hideMessage(); hideWaitAnimation(); - button_actedit.html("Edit"); button_actmem.html("Memory"); button_actwi.html("W Info"); hideAidgPopup(); @@ -638,6 +621,74 @@ function setadventure(state) { } } +function autofocus(event) { + if(connected) { + current_chunk_changed = true; + event.target.focus(); + } else { + event.preventDefault(); + } +} + +function chunkOnKeyDown(event) { + // Enter should submit the chunk changes, except when holding shift + // Escape should also do it regardless of whether shift is held + if(event.keyCode == 27 || (!event.shiftKey && event.keyCode == 13)) { + event.target.blur(); + event.preventDefault(); + return; + } + + // Don't allow any edits if not connected to server + if(!connected) { + event.preventDefault(); + return; + } + + // Prevent CTRL+B, CTRL+I and CTRL+U when editing chunks + if(event.ctrlKey || event.metaKey) { // metaKey is macOS's command key + switch(event.keyCode) { + case 66: + case 98: + case 73: + case 105: + case 85: + case 117: + event.preventDefault(); + return; + } + } +} + +function submitEditedChunk(event) { + // Don't do anything if the current chunk hasn't been edited or if someone + // else overwrote it while you were busy lollygagging + if(!current_chunk_changed || chunk_conflict) { + chunk_conflict = false; + return; + } + + show([$('#curtain')]); + setTimeout(function () { + document.activeElement.blur(); + }, 5); + document.activeElement.blur(); + current_chunk_changed = false; + + // Enter edit mode if we aren't already in edit mode + if(!editmode) { + socket.send({'cmd': 'edit', 'data': ''}); + } + + // Submit the edited chunk if it's not empty, otherwise delete it + socket.send({'cmd': 'editline', 'data': event.target.getAttribute("n")}); + if(event.target.innerText.length) { + socket.send({'cmd': 'submit', 'data': event.target.innerText}); + } else { + socket.send({'cmd': 'delete', 'data': ''}); + } +} + //=================================================================// // READY/RUNTIME //=================================================================// @@ -661,7 +712,6 @@ $(document).ready(function(){ button_mode = $('#btnmode') button_mode_label = $('#btnmode_label') button_send = $('#btnsend'); - button_actedit = $('#btn_actedit'); button_actmem = $('#btn_actmem'); button_actback = $('#btn_actundo'); button_actretry = $('#btn_actretry'); @@ -711,6 +761,7 @@ $(document).ready(function(){ socket.on('from_server', function(msg) { if(msg.cmd == "connected") { // Connected to Server Actions + connected = true; connect_status.html("Connected to KoboldAI Process!"); connect_status.removeClass("color_orange"); connect_status.addClass("color_green"); @@ -718,6 +769,14 @@ $(document).ready(function(){ settings_menu.html(""); format_menu.html(""); wi_menu.html(""); + // Set up "Allow Editing" + $('body').on('input', autofocus).on('keydown', 'chunk', chunkOnKeyDown).on('focusout', 'chunk', submitEditedChunk); + $('#allowediting').prop('checked', allowedit).prop('disabled', false).change().on('change', function () { + if(allowtoggle) { + checked = $(this).prop('checked') + $("chunk").attr('contenteditable', checked) + } + }); } else if(msg.cmd == "updatescreen") { _gamestarted = gamestarted gamestarted = msg.gamestarted; @@ -726,20 +785,30 @@ $(document).ready(function(){ changemode(); } // Send game content to Game Screen + if(allowedit && document.activeElement.tagName == "CHUNK") { + chunk_conflict = true; + } game_text.html(msg.data); + // Make content editable if need be + $("chunk").attr('tabindex', -1) + $('chunk').attr('contenteditable', allowedit); + hide([$('#curtain')]); // Scroll to bottom of text - setTimeout(function () { - $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); - }, 5); + if(newly_loaded) { + setTimeout(function () { + $('#gamescreen').animate({scrollTop: $('#gamescreen').prop('scrollHeight')}, 1000); + }, 5); + } + newly_loaded = false; } else if(msg.cmd == "setgamestate") { // Enable or Disable buttons if(msg.data == "ready") { enableSendBtn(); - enableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + enableButtons([button_actmem, button_actwi, button_actback, button_actretry]); hideWaitAnimation(); } else if(msg.data == "wait") { disableSendBtn(); - disableButtons([button_actedit, button_actmem, button_actwi, button_actback, button_actretry]); + disableButtons([button_actmem, button_actwi, button_actback, button_actretry]); showWaitAnimation(); } else if(msg.data == "start") { setStartState(); @@ -752,11 +821,10 @@ $(document).ready(function(){ exitEditMode(); } } else if(msg.cmd == "setinputtext") { - // Set input box text for edit mode - input_text.val(msg.data); - } else if(msg.cmd == "enablesubmit") { - // Enables the submit button - enableSendBtn(); + // Set input box text for memory mode + if(memorymode) { + input_text.val(msg.data); + } } else if(msg.cmd == "memmode") { // Enable or Disable memory edit mode if(msg.data == "true") { @@ -932,6 +1000,7 @@ $(document).ready(function(){ }); socket.on('disconnect', function() { + connected = false; connect_status.html("Lost connection..."); connect_status.removeClass("color_green"); connect_status.addClass("color_orange"); @@ -956,10 +1025,6 @@ $(document).ready(function(){ hidegenseqs(); }); - button_actedit.on("click", function(ev) { - socket.send({'cmd': 'edit', 'data': ''}); - }); - button_delete.on("click", function(ev) { socket.send({'cmd': 'delete', 'data': ''}); }); @@ -1042,6 +1107,7 @@ $(document).ready(function(){ }); load_accept.on("click", function(ev) { + newly_loaded = true; socket.send({'cmd': 'loadrequest', 'data': ''}); hideLoadPopup(); }); diff --git a/static/custom.css b/static/custom.css index be2dce70..e821d9c7 100644 --- a/static/custom.css +++ b/static/custom.css @@ -11,6 +11,11 @@ action { font-weight: bold; } +chunk[contenteditable="true"]:focus, chunk[contenteditable="true"]:focus > action { + color: #ddffdd; + font-weight: normal; +} + #topmenu { background-color: #337ab7; padding: 10px; @@ -58,7 +63,7 @@ action { } #gamescreen { - height: 500px; + height: 490px; margin-top: 10px; padding: 10px; display: flex; @@ -77,6 +82,7 @@ action { #gametext { max-height: 100%; width: 100%; + word-wrap: break-word; } #seqselmenu { @@ -184,7 +190,7 @@ action { position: absolute; top: 0px; left: 0px; - z-index: 1; + z-index: 3; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); @@ -318,6 +324,22 @@ action { width: 100px; } +.box { + border-radius: 5px; + border: 1px solid #646464; + padding: 4px; + background: #373737; +} + +.box-label { + color: #ffffff; + padding-left: 10px; + padding-right: 10px; + padding-bottom: 5px; + padding-top: 5px; + display: inline-block; +} + .chunkhov:hover { color: #c0fc51; cursor: pointer; @@ -327,7 +349,7 @@ action { color: #00fa00; } -.colorfade { +.colorfade, .colorfade > action { -moz-transition:color 1s ease-in; -o-transition:color 1s ease-in; -webkit-transition:color 1s ease-in; @@ -371,8 +393,17 @@ action { text-decoration: none; } +.edit-flash, .edit-flash > action { + color: #3bf723; +} + .flex { display: flex; + align-items: center; +} + +.flex-push-right { + margin-left: auto; } .formatcolumn { @@ -458,6 +489,20 @@ action { text-align: right; } +.layer-container { + display: grid; +} + +.layer-bottom { + grid-area: 1/1; + z-index: 0; +} + +.layer-top { + grid-area: 1/1; + z-index: 2; +} + .loadlistheader { padding-left: 10px; display: grid; @@ -531,11 +576,15 @@ action { } +.nowrap { + white-space: nowrap; +} + .popupcontainer { position: absolute; top: 0px; left: 0px; - z-index: 1; + z-index: 3; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); @@ -652,6 +701,10 @@ action { width: 50px; } +.width-auto { + width: auto; +} + .wilistitem { height: 80px; display: grid; diff --git a/templates/index.html b/templates/index.html index 4b9ac8f7..55655481 100644 --- a/templates/index.html +++ b/templates/index.html @@ -76,27 +76,31 @@
-
- ... -