Merge pull request #74 from ebolam/united

Redo, Pinning, and docker enhancements
This commit is contained in:
henk717 2022-02-07 01:06:36 +01:00 committed by GitHub
commit e1ef4e4fa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 360 additions and 49 deletions

View File

@ -121,6 +121,12 @@ class vars:
setauthornotetemplate = authornotetemplate # Saved author's note template in settings
andepth = 3 # How far back in history to append author's note
actions = structures.KoboldStoryRegister() # Actions submitted by user and AI
actions_metadata = [] # List of dictonaries, one dictonary for every action that contains information about the action like alternative options.
# Contains at least the same number of items as actions. Back action will remove an item from actions, but not actions_metadata
# Dictonary keys are:
# Selected Text: (text the user had selected. None when this is a newly generated action)
# Alternative Generated Text: {Text, Pinned, Previous Selection, Edited}
#
worldinfo = [] # List of World Info key/value objects
worldinfo_i = [] # List of World Info key/value objects sans uninitialized entries
worldinfo_u = {} # Dictionary of World Info UID - key/value pairs
@ -202,6 +208,8 @@ class vars:
nogenmod = False
welcome = False # Custom Welcome Text (False is default)
newlinemode = "n"
quiet = False # If set will suppress any story text from being printed to the console (will only be seen on the client web page)
debug = False # If set to true, will send debug information to the client for display
#==================================================================#
# Function to get model selection at startup
@ -465,6 +473,8 @@ parser.add_argument("--override_rename", action='store_true', help="Renaming sto
parser.add_argument("--configname", help="Force a fixed configuration name to aid with config management.")
parser.add_argument("--colab", action='store_true', help="Optimize for Google Colab.")
parser.add_argument("--nobreakmodel", action='store_true', help="Disables Breakmodel support completely.")
parser.add_argument("--share", action='store_true', default=False, help="If present will launch KoboldAI available to all computers rather than local only")
parser.add_argument("--quiet", action='store_true', default=False, help="If present will suppress any story related text from showing on the console")
args: argparse.Namespace = None
if(os.environ.get("KOBOLDAI_ARGS") is not None):
@ -475,6 +485,9 @@ else:
vars.model = args.model;
if args.quiet:
vars.quiet = True
if args.colab:
args.remote = True;
args.override_rename = True;
@ -972,43 +985,50 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
lowmem = {}
# Download model from Huggingface if it does not exist, otherwise load locally
#If we specify a model and it's in the root directory, we need to move it to the models directory (legacy folder structure to new)
if os.path.isdir(vars.model.replace('/', '_')):
import shutil
shutil.move(vars.model.replace('/', '_'), "models/{}".format(vars.model.replace('/', '_')))
if(os.path.isdir(vars.custmodpth)):
with(maybe_use_float16()):
try:
tokenizer = AutoTokenizer.from_pretrained(vars.custmodpth, cache_dir="cache/")
tokenizer = AutoTokenizer.from_pretrained(vars.custmodpth, cache_dir="cache")
except ValueError as e:
tokenizer = GPT2TokenizerFast.from_pretrained(vars.custmodpth, cache_dir="cache/")
tokenizer = GPT2TokenizerFast.from_pretrained(vars.custmodpth, cache_dir="cache")
try:
model = AutoModelForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache/", **lowmem)
model = AutoModelForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache", **lowmem)
except ValueError as e:
model = GPTNeoForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache/", **lowmem)
elif(os.path.isdir(vars.model.replace('/', '_'))):
model = GPTNeoForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache", **lowmem)
elif(os.path.isdir("models/{}".format(vars.model.replace('/', '_')))):
with(maybe_use_float16()):
try:
tokenizer = AutoTokenizer.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/")
tokenizer = AutoTokenizer.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache")
except ValueError as e:
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/")
tokenizer = GPT2TokenizerFast.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache")
try:
model = AutoModelForCausalLM.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/", **lowmem)
model = AutoModelForCausalLM.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache", **lowmem)
except ValueError as e:
model = GPTNeoForCausalLM.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/", **lowmem)
model = GPTNeoForCausalLM.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache", **lowmem)
else:
try:
tokenizer = AutoTokenizer.from_pretrained(vars.model, cache_dir="cache/")
tokenizer = AutoTokenizer.from_pretrained(vars.model, cache_dir="cache")
except ValueError as e:
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model, cache_dir="cache/")
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model, cache_dir="cache")
with(maybe_use_float16()):
try:
model = AutoModelForCausalLM.from_pretrained(vars.model, cache_dir="cache/", **lowmem)
model = AutoModelForCausalLM.from_pretrained(vars.model, cache_dir="cache", **lowmem)
except ValueError as e:
model = GPTNeoForCausalLM.from_pretrained(vars.model, cache_dir="cache/", **lowmem)
model = GPTNeoForCausalLM.from_pretrained(vars.model, cache_dir="cache", **lowmem)
if not args.colab:
model = model.half()
print("Trying to save model")
import shutil
shutil.rmtree("cache/")
model.save_pretrained(vars.model.replace('/', '_'))
tokenizer.save_pretrained(vars.model.replace('/', '_'))
model = model.half()
model.save_pretrained("models/{}".format(vars.model.replace('/', '_')))
tokenizer.save_pretrained("models/{}".format(vars.model.replace('/', '_')))
print("Saved")
if(vars.hascuda):
if(vars.usegpu):
@ -1173,6 +1193,7 @@ def download():
js["authorsnote"] = vars.authornote
js["anotetemplate"] = vars.authornotetemplate
js["actions"] = tuple(vars.actions.values())
js["actions_metadata"] = vars.actions_metadata
js["worldinfo"] = []
# Extract only the important bits of WI
@ -1637,7 +1658,11 @@ def lua_set_chunk(k, v):
del vars._actions[chunk-1]
vars.lua_deleted.add(chunk)
if(not hasattr(vars, "_actions") or vars._actions is not vars.actions):
del vars.actions[chunk-1]
#Instead of deleting we'll blank out the text. This way our actions and actions_metadata stay in sync and we can restore the chunk on an undo
vars.actions[chunk-1] = ""
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions_metadata[chunk-1]['Selected Text'], "Pinned": False, "Editted": True}] + vars.actions_metadata[chunk-1]['Alternative Text']
vars.actions_metadata[chunk-1]['Selected Text'] = ''
send_debug()
else:
if(k == 0):
print(colors.GREEN + f"{lua_log_format_name(vars.lua_koboldbridge.logging_name)} edited prompt chunk" + colors.END)
@ -1654,6 +1679,9 @@ def lua_set_chunk(k, v):
vars._actions[chunk-1] = v
vars.lua_edited.add(chunk)
vars.actions[chunk-1] = v
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions_metadata[chunk-1]['Selected Text'], "Pinned": False, "Editted": True}] + vars.actions_metadata[chunk-1]['Alternative Text']
vars.actions_metadata[chunk-1]['Selected Text'] = v
send_debug()
#==================================================================#
# Get model type as "gpt-2-xl", "gpt-neo-2.7B", etc.
@ -1843,6 +1871,7 @@ def do_connect():
#==================================================================#
@socketio.on('message')
def get_message(msg):
if not vars.quiet:
print("{0}Data received:{1}{2}".format(colors.GREEN, msg, colors.END))
# Submit action
if(msg['cmd'] == 'submit'):
@ -1882,6 +1911,9 @@ def get_message(msg):
# Back/Undo Action
elif(msg['cmd'] == 'back'):
actionback()
# Forward/Redo Action
elif(msg['cmd'] == 'redo'):
actionredo()
# EditMode Action (old)
elif(msg['cmd'] == 'edit'):
if(vars.mode == "play"):
@ -2119,6 +2151,8 @@ def get_message(msg):
vars.saveow = False
elif(msg['cmd'] == 'seqsel'):
selectsequence(msg['data'])
elif(msg['cmd'] == 'seqpin'):
pinsequence(msg['data'])
elif(msg['cmd'] == 'setnumseq'):
vars.numseqs = int(msg['data'])
emit('from_server', {'cmd': 'setlabelnumseq', 'data': msg['data']})
@ -2165,6 +2199,11 @@ def get_message(msg):
refresh_settings()
elif(not vars.remote and msg['cmd'] == 'importwi'):
wiimportrequest()
elif(msg['cmd'] == 'debug'):
vars.debug = msg['data']
emit('from_server', {'cmd': 'set_debug', 'data': msg['data']}, broadcast=True)
if vars.debug:
send_debug()
#==================================================================#
# Send userscripts list to client
@ -2493,7 +2532,25 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False,
vars.prompt = data
else:
vars.actions.append(data)
# we now need to update the actions_metadata
# we'll have two conditions.
# 1. This is totally new (user entered)
if len(vars.actions_metadata) < len(vars.actions):
vars.actions_metadata.append({"Selected Text": data, "Alternative Text": []})
else:
# 2. We've selected a chunk of text that is was presented previously
try:
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)-1]["Alternative Text"]]
except:
print(len(vars.actions))
print(vars.actions_metadata)
raise
if data in alternatives:
alternatives = [item for item in vars.actions_metadata[len(vars.actions)-1]["Alternative Text"] if item['Text'] != data]
vars.actions_metadata[len(vars.actions)-1]["Alternative Text"] = alternatives
vars.actions_metadata[len(vars.actions)-1]["Selected Text"] = data
update_story_chunk('last')
send_debug()
if(not vars.noai and vars.lua_koboldbridge.generating):
# Off to the tokenizer!
@ -2548,6 +2605,15 @@ def actionretry(data):
# Remove last action if possible and resubmit
if(vars.gamestarted if vars.useprompt else len(vars.actions) > 0):
if(not vars.recentback and len(vars.actions) != 0 and len(vars.genseqs) == 0): # Don't pop if we're in the "Select sequence to keep" menu or if there are no non-prompt actions
# We are going to move the selected text to alternative text in the actions_metadata variable so we can redo this action
vars.actions_metadata[vars.actions]['Alternative Text'] = [{'Text': vars.actions_metadata[len(vars.actions)]['Selected Text'],
'Pinned': False,
"Previous Selection": True,
"Edited": False}] + vars.actions_metadata[vars.actions]['Alternative Text']
vars.actions_metadata[vars.actions]['Selected Text'] = ""
last_key = vars.actions.get_last_key()
vars.actions.pop()
remove_story_chunk(last_key + 1)
@ -2555,6 +2621,7 @@ def actionretry(data):
vars.recentedit = False
vars.lua_koboldbridge.feedback = None
actionsubmit("", actionmode=vars.actionmode, force_submit=True)
send_debug()
elif(not vars.useprompt):
emit('from_server', {'cmd': 'errmsg', 'data': "Please enable \"Always Add Prompt\" to retry with your prompt."})
@ -2566,6 +2633,13 @@ def actionback():
return
# Remove last index of actions and refresh game screen
if(len(vars.genseqs) == 0 and len(vars.actions) > 0):
# We are going to move the selected text to alternative text in the actions_metadata variable so we can redo this action
vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] = [{'Text': vars.actions_metadata[len(vars.actions)-1]['Selected Text'],
'Pinned': False,
"Previous Selection": True,
"Edited": False}] + vars.actions_metadata[len(vars.actions)-1]['Alternative Text']
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = ""
last_key = vars.actions.get_last_key()
vars.actions.pop()
vars.recentback = True
@ -2574,6 +2648,28 @@ def actionback():
emit('from_server', {'cmd': 'errmsg', 'data': "Cannot delete the prompt."})
else:
vars.genseqs = []
send_debug()
def actionredo():
i = 0
if len(vars.actions) < len(vars.actions_metadata):
genout = [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==True)]
genout = genout + [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Pinned"]==True) and (item["Previous Selection"]==False)]
if len(genout) == 1:
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = [item for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]!=True)]
genresult(genout[0]['generated_text'], flash=True)
else:
# Store sequences in memory until selection is made
vars.genseqs = genout
# Send sequences to UI for selection
genout = [[item['Text'], "redo"] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==True)]
emit('from_server', {'cmd': 'genseqs', 'data': genout}, broadcast=True)
else:
emit('from_server', {'cmd': 'popuperror', 'data': "There's nothing to undo"}, broadcast=True)
send_debug()
#==================================================================#
#
@ -2889,6 +2985,7 @@ def generate(txt, minimum, maximum, found_entries=None):
found_entries = set()
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
if not vars.quiet:
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, tokenizer.decode(txt), colors.END))
# Store context in memory to use it for comparison with generated content
@ -2951,6 +3048,7 @@ def generate(txt, minimum, maximum, found_entries=None):
# Deal with a single return sequence from generate()
#==================================================================#
def genresult(genout, flash=True):
if not vars.quiet:
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
# Format output before continuing
@ -2966,9 +3064,14 @@ def genresult(genout, flash=True):
vars.prompt = genout
else:
vars.actions.append(genout)
if len(vars.actions) > len(vars.actions_metadata):
vars.actions_metadata.append({'Selected Text': genout, 'Alternative Text': []})
else:
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = genout
update_story_chunk('last')
if(flash):
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
send_debug()
#==================================================================#
# Send generator sequences to the UI for selection
@ -2978,14 +3081,34 @@ def genselect(genout):
for result in genout:
# Apply output formatting rules to sequences
result["generated_text"] = applyoutputformatting(result["generated_text"])
if not vars.quiet:
print("{0}[Result {1}]\n{2}{3}".format(colors.CYAN, i, result["generated_text"], colors.END))
i += 1
# Add the options to the actions metadata
# If we've already generated text for this action but haven't selected one we'll want to kill all non-pinned, non-previous selection, and non-edited options then add the new ones
if (len(vars.actions_metadata) > len(vars.actions)):
if (vars.actions_metadata[len(vars.actions)]['Selected Text'] == ""):
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = [{"Text": item['Text'], "Pinned": item['Pinned'],
"Previous Selection": item["Previous Selection"],
"Edited": item["Edited"]} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text']
if item['Pinned'] or item["Previous Selection"] or item["Edited"]] + [{"Text": text["generated_text"],
"Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]
else:
vars.actions_metadata.append({'Selected Text': '', 'Alternative Text': [{"Text": text["generated_text"], "Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]})
else:
vars.actions_metadata.append({'Selected Text': '', 'Alternative Text': [{"Text": text["generated_text"], "Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]})
genout = [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==False) and (item["Edited"]==False)]
# Store sequences in memory until selection is made
vars.genseqs = genout
genout = [[item['Text'], "pinned" if item['Pinned'] else "normal"] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==False) and (item["Edited"]==False)]
# Send sequences to UI for selection
emit('from_server', {'cmd': 'genseqs', 'data': genout}, broadcast=True)
send_debug()
#==================================================================#
# Send selected sequence to action log and refresh UI
@ -2996,6 +3119,9 @@ def selectsequence(n):
vars.lua_koboldbridge.feedback = vars.genseqs[int(n)]["generated_text"]
if(len(vars.lua_koboldbridge.feedback) != 0):
vars.actions.append(vars.lua_koboldbridge.feedback)
#We'll want to remove the option from the alternative text and put it in selected text
vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] = [item for item in vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] if item['Text'] != vars.lua_koboldbridge.feedback]
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = vars.lua_koboldbridge.feedback
update_story_chunk('last')
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True)
@ -3003,12 +3129,30 @@ def selectsequence(n):
if(vars.lua_koboldbridge.restart_sequence is not None):
actionsubmit("", actionmode=vars.actionmode, force_submit=True, disable_recentrng=True)
send_debug()
#==================================================================#
# Pin/Unpin the selected sequence
#==================================================================#
def pinsequence(n):
if n.isnumeric():
text = vars.genseqs[int(n)]['generated_text']
if text in [item['Text'] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text']]:
alternatives = vars.actions_metadata[len(vars.actions)]['Alternative Text']
for i in range(len(alternatives)):
if alternatives[i]['Text'] == text:
alternatives[i]['Pinned'] = not alternatives[i]['Pinned']
break
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = alternatives
send_debug()
#==================================================================#
# Send transformers-style request to ngrok/colab host
#==================================================================#
def sendtocolab(txt, min, max):
# Log request to console
if not vars.quiet:
print("{0}Tokens:{1}, Txt:{2}{3}".format(colors.YELLOW, min-1, txt, colors.END))
# Store context in memory to use it for comparison with generated content
@ -3094,6 +3238,7 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
found_entries = set()
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
if not vars.quiet:
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, tokenizer.decode(txt), colors.END))
vars._actions = vars.actions
@ -3411,12 +3556,17 @@ def editsubmit(data):
if(vars.editln == 0):
vars.prompt = data
else:
vars.actions_metadata[vars.editln-1]['Alternative Text'] = vars.actions_metadata[vars.editln-1]['Alternative Text'] + [{"Text": vars.actions[vars.editln-1], "Pinned": False,
"Previous Selection": False,
"Edited": True}]
vars.actions_metadata[vars.editln-1]['Selected Text'] = data
vars.actions[vars.editln-1] = data
vars.mode = "play"
update_story_chunk(vars.editln)
emit('from_server', {'cmd': 'texteffect', 'data': vars.editln}, broadcast=True)
emit('from_server', {'cmd': 'editmode', 'data': 'false'})
send_debug()
#==================================================================#
#
@ -3428,10 +3578,14 @@ def deleterequest():
# Send error message
pass
else:
del vars.actions[vars.editln-1]
vars.actions_metadata[vars.editln-1]['Alternative Text'] = [{"Text": vars.actions[vars.editln-1], "Pinned": False,
"Previous Selection": True, "Edited": False}] + vars.actions_metadata[vars.editln-1]['Alternative Text']
vars.actions_metadata[vars.editln-1]['Selected Text'] = ''
vars.actions[vars.editln-1] = ''
vars.mode = "play"
remove_story_chunk(vars.editln)
emit('from_server', {'cmd': 'editmode', 'data': 'false'})
send_debug()
#==================================================================#
#
@ -3445,6 +3599,10 @@ def inlineedit(chunk, data):
vars.prompt = data
else:
if(chunk-1 in vars.actions):
vars.actions_metadata[chunk-1]['Alternative Text'] = vars.actions_metadata[chunk-1]['Alternative Text'] + [{"Text": vars.actions[chunk-1], "Pinned": False,
"Previous Selection": False,
"Edited": True}]
vars.actions_metadata[chunk-1]['Selected Text'] = data
vars.actions[chunk-1] = data
else:
print(f"WARNING: Attempted to edit non-existent chunk {chunk}")
@ -3453,6 +3611,7 @@ def inlineedit(chunk, data):
update_story_chunk(chunk)
emit('from_server', {'cmd': 'texteffect', 'data': chunk}, broadcast=True)
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
send_debug()
#==================================================================#
#
@ -3468,12 +3627,17 @@ def inlinedelete(chunk):
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
else:
if(chunk-1 in vars.actions):
del vars.actions[chunk-1]
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions[chunk-1], "Pinned": False,
"Previous Selection": True,
"Edited": False}] + vars.actions_metadata[chunk-1]['Alternative Text']
vars.actions_metadata[chunk-1]['Selected Text'] = ''
vars.actions[chunk-1] = ''
else:
print(f"WARNING: Attempted to delete non-existent chunk {chunk}")
setgamesaved(False)
remove_story_chunk(chunk)
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
send_debug()
#==================================================================#
# Toggles the game mode for memory editing and sends UI commands
@ -3822,6 +3986,7 @@ def anotesubmit(data, template=""):
#==================================================================#
def ikrequest(txt):
# Log request to console
if not vars.quiet:
print("{0}Len:{1}, Txt:{2}{3}".format(colors.YELLOW, len(txt), txt, colors.END))
# Build request JSON data
@ -3859,11 +4024,21 @@ def ikrequest(txt):
genout = vars.lua_koboldbridge.outputs[1]
assert genout is str
if not vars.quiet:
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
vars.actions.append(genout)
if len(vars.actions_metadata) < len(vars.actions):
vars.actions_metadata.append({"Selected Text": genout, "Alternative Text": []})
else:
# 2. We've selected a chunk of text that is was presented previously
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"]]
if genout in alternatives:
alternatives = [item for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"] if item['Text'] != genout]
vars.actions_metadata[len(vars.actions)]["Alternative Text"] = alternatives
vars.actions_metadata[len(vars.actions)]["Selected Text"] = genout
update_story_chunk('last')
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
send_debug()
set_aibusy(0)
else:
# Send error message to web client
@ -3882,6 +4057,7 @@ def ikrequest(txt):
#==================================================================#
def oairequest(txt, min, max):
# Log request to console
if not vars.quiet:
print("{0}Len:{1}, Txt:{2}{3}".format(colors.YELLOW, len(txt), txt, colors.END))
# Store context in memory to use it for comparison with generated content
@ -3918,11 +4094,21 @@ def oairequest(txt, min, max):
genout = vars.lua_koboldbridge.outputs[1]
assert genout is str
if not vars.quiet:
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
vars.actions.append(genout)
if len(vars.actions_metadata) < len(vars.actions):
vars.actions_metadata.append({"Selected Text": genout, "Alternative Text": []})
else:
# 2. We've selected a chunk of text that is was presented previously
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"]]
if genout in alternatives:
alternatives = [item for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"] if item['Text'] != genout]
vars.actions_metadata[len(vars.actions)]["Alternative Text"] = alternatives
vars.actions_metadata[len(vars.actions)]["Selected Text"] = genout
update_story_chunk('last')
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
send_debug()
set_aibusy(0)
else:
# Send error message to web client
@ -3950,12 +4136,15 @@ def exitModes():
#==================================================================#
# Launch in-browser save prompt
#==================================================================#
def saveas(name):
def saveas(data):
name = data['name']
savepins = data['pins']
# Check if filename exists already
name = utils.cleanfilename(name)
if(not fileops.saveexists(name) or (vars.saveow and vars.svowname == name)):
# All clear to save
e = saveRequest(fileops.storypath(name))
e = saveRequest(fileops.storypath(name), savepins=savepins)
vars.saveow = False
vars.svowname = ""
if(e is None):
@ -4031,7 +4220,7 @@ def savetofile():
#==================================================================#
# Save the story to specified path
#==================================================================#
def saveRequest(savpath):
def saveRequest(savpath, savepins=True):
if(savpath):
# Leave Edit/Memory mode before continuing
exitModes()
@ -4047,6 +4236,8 @@ def saveRequest(savpath):
js["authorsnote"] = vars.authornote
js["anotetemplate"] = vars.authornotetemplate
js["actions"] = tuple(vars.actions.values())
if savepins:
js["actions_metadata"] = vars.actions_metadata
js["worldinfo"] = []
js["wifolders_d"] = vars.wifolders_d
js["wifolders_l"] = vars.wifolders_l
@ -4172,6 +4363,12 @@ def loadRequest(loadpath, filename=None):
vars.actions = structures.KoboldStoryRegister()
actions = collections.deque(js["actions"])
if "actions_metadata" in js:
vars.actions_metadata = js["actions_metadata"]
else:
vars.actions_metadata = [{'Selected Text': text, 'Alternative Text': []} for text in js["actions"]]
if(len(vars.prompt.strip()) == 0):
while(len(actions)):
action = actions.popleft()
@ -4588,6 +4785,7 @@ def wiimportrequest():
if(vars.worldinfo[-1]["folder"] is not None):
vars.wifolders_u[vars.worldinfo[-1]["folder"]].append(vars.worldinfo[-1])
if not vars.quiet:
print("{0}".format(vars.worldinfo[0]))
# Refresh game screen
@ -4606,6 +4804,7 @@ def newGameRequest():
vars.prompt = ""
vars.memory = ""
vars.actions = structures.KoboldStoryRegister()
vars.actions_metadata = []
vars.authornote = ""
vars.authornotetemplate = vars.setauthornotetemplate
@ -4689,6 +4888,13 @@ if(vars.model in ("TPUMeshTransformerGPTJ",)):
},
).start()
def send_debug():
if vars.debug:
debug_info = ""
for variable in [["Action Length", len(vars.actions)], ["Actions Metadata Length", len(vars.actions_metadata)], ["Actions Metadata", vars.actions_metadata]]:
debug_info = "{}{}: {}\n".format(debug_info, variable[0], variable[1])
emit('from_server', {'cmd': 'debug_info', 'data': debug_info}, broadcast=True)
#==================================================================#
# Final startup commands to launch Flask app
#==================================================================#
@ -4716,6 +4922,9 @@ if __name__ == "__main__":
webbrowser.open_new('http://localhost:5000')
print("{0}Server started!\nYou may now connect with a browser at http://127.0.0.1:5000/{1}".format(colors.GREEN, colors.END))
vars.serverstarted = True
if args.share:
socketio.run(app, port=5000, host='0.0.0.0')
else:
socketio.run(app, port=5000)
else:

20
environments/base.yml Normal file
View File

@ -0,0 +1,20 @@
name: koboldai
channels:
- pytorch
- conda-forge
- defaults
dependencies:
- colorama
- flask-socketio
- pytorch
- python=3.8.*
- cudatoolkit=11.1
- eventlet
- markdown
- bleach
- pip
- git
- pip:
- flask-cloudflared
- flask-ngrok
- lupa==1.10

View File

@ -207,6 +207,17 @@ gensettingstf = [
"step": 1,
"default": 0,
"tooltip": "Disables userscript generation modifiers."
},
{
"uitype": "toggle",
"unit": "bool",
"label": "Debug",
"id": "debug",
"min": 0,
"max": 1,
"step": 1,
"default": 0,
"tooltip": "Show debug info"
}
]
@ -341,6 +352,17 @@ gensettingsik =[{
"step": 1,
"default": 0,
"tooltip": "When enabled, the Memory text box in the Random Story dialog will be prefilled by default with your current story's memory instead of being empty."
},
{
"uitype": "toggle",
"unit": "bool",
"label": "Debug",
"id": "debug",
"min": 0,
"max": 1,
"step": 1,
"default": 0,
"tooltip": "Show debug info"
}
]

View File

View File

@ -25,6 +25,7 @@ var button_mode_label;
var button_send;
var button_actmem;
var button_actback;
var button_actfwd;
var button_actretry;
var button_actwi;
var game_text;
@ -38,6 +39,7 @@ var anote_menu;
var anote_input;
var anote_labelcur;
var anote_slider;
var debug_area;
var popup;
var popup_title;
var popup_content;
@ -49,6 +51,7 @@ var aidg_accept;
var aidg_close;
var saveaspopup;
var saveasinput;
var savepins;
var topic;
var saveas_accept;
var saveas_close;
@ -748,7 +751,7 @@ function enterMemoryMode() {
setchatnamevisibility(false);
showMessage("Edit the memory to be sent with each request to the AI.");
button_actmem.html("Cancel");
hide([button_actback, button_actretry, button_actwi]);
hide([button_actback, button_actfwd, button_actretry, button_actwi]);
// Display Author's Note field
anote_menu.slideDown("fast");
}
@ -759,7 +762,7 @@ function exitMemoryMode() {
setchatnamevisibility(chatmode);
hideMessage();
button_actmem.html("Memory");
show([button_actback, button_actretry, button_actwi]);
show([button_actback, button_actfwd, button_actretry, button_actwi]);
input_text.val("");
// Hide Author's Note field
anote_menu.slideUp("fast");
@ -768,7 +771,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_actback, button_actmem, button_actretry, game_text]);
hide([button_actback, button_actfwd, button_actmem, button_actretry, game_text]);
setchatnamevisibility(false);
show([wi_menu]);
disableSendBtn();
@ -780,7 +783,7 @@ function exitWiMode() {
button_actwi.html("W Info");
hide([wi_menu]);
setchatnamevisibility(chatmode);
show([button_actback, button_actmem, button_actretry, game_text]);
show([button_actback, button_actfwd, button_actmem, button_actretry, game_text]);
enableSendBtn();
$("#gamescreen").removeClass("wigamescreen");
}
@ -884,7 +887,7 @@ function hideSaveAsPopup() {
}
function sendSaveAsRequest() {
socket.send({'cmd': 'saveasrequest', 'data': saveasinput.val()});
socket.send({'cmd': 'saveasrequest', 'data': {"name": saveasinput.val(), "pins": savepins.val()}});
}
function showLoadPopup() {
@ -1142,9 +1145,9 @@ function updateSPStatItems(items) {
function setStartState() {
enableSendBtn();
enableButtons([button_actmem, button_actwi]);
disableButtons([button_actback, button_actretry]);
disableButtons([button_actback, button_actfwd, button_actretry]);
hide([wi_menu]);
show([game_text, button_actmem, button_actwi, button_actback, button_actretry]);
show([game_text, button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
hideMessage();
hideWaitAnimation();
button_actmem.html("Memory");
@ -1160,10 +1163,41 @@ function parsegenseqs(seqs) {
seqselcontents.html("");
var i;
for(i=0; i<seqs.length; i++) {
seqselcontents.append("<div class=\"seqselitem\" id=\"seqsel"+i+"\" n=\""+i+"\">"+seqs[i].generated_text+"</div>");
//setup selection data
text_data = "<table><tr><td width=100%><div class=\"seqselitem\" id=\"seqsel"+i+"\" n=\""+i+"\">"+seqs[i][0]+"</div></td><td width=10>"
//Now do the icon (pin/redo)
if (seqs[i][1] == "redo") {
text_data = text_data + "<span style=\"color: white\" class=\"oi oi-loop-circular\" title=\"Redo\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
} else if (seqs[i][1] == "pinned") {
text_data = text_data + "<span style=\"color: white\" class=\"oi oi-pin\" title=\"Pin\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
} else {
text_data = text_data + "<span style=\"color: grey\" class=\"oi oi-pin\" title=\"Pin\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
}
text_data = text_data + "</td></tr></table>"
seqselcontents.append(text_data);
//setup on-click actions
$("#seqsel"+i).on("click", function () {
socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")});
});
//onclick for pin only
if (seqs[i][1] != "redo") {
$("#seqselpin"+i).on("click", function () {
socket.send({'cmd': 'seqpin', 'data': $(this).attr("n")});
if ($(this).attr("style") == "color: grey") {
console.log($(this).attr("style"));
$(this).css({"color": "white"});
console.log($(this).attr("style"));
} else {
console.log($(this).attr("style"));
$(this).css({"color": "grey"});
console.log($(this).attr("style"));
}
});
}
}
$('#seqselmenu').slideDown("slow");
}
@ -1741,6 +1775,7 @@ $(document).ready(function(){
button_send = $('#btnsend');
button_actmem = $('#btn_actmem');
button_actback = $('#btn_actundo');
button_actfwd = $('#btn_actredo');
button_actretry = $('#btn_actretry');
button_actwi = $('#btn_actwi');
game_text = $('#gametext');
@ -1750,6 +1785,7 @@ $(document).ready(function(){
settings_menu = $("#settingsmenu");
format_menu = $('#formatmenu');
anote_menu = $('#anoterowcontainer');
debug_area = $('#debugcontainer');
wi_menu = $('#wimenu');
anote_input = $('#anoteinput');
anote_labelcur = $('#anotecur');
@ -1765,6 +1801,7 @@ $(document).ready(function(){
aidg_close = $("#btn_aidgpopupclose");
saveaspopup = $("#saveascontainer");
saveasinput = $("#savename");
savepins = $("#savepins");
topic = $("#topic");
saveas_accept = $("#btn_saveasaccept");
saveas_close = $("#btn_saveasclose");
@ -1913,13 +1950,13 @@ $(document).ready(function(){
// Enable or Disable buttons
if(msg.data == "ready") {
enableSendBtn();
enableButtons([button_actmem, button_actwi, button_actback, button_actretry]);
enableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
hideWaitAnimation();
gamestate = "ready";
} else if(msg.data == "wait") {
gamestate = "wait";
disableSendBtn();
disableButtons([button_actmem, button_actwi, button_actback, button_actretry]);
disableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
showWaitAnimation();
} else if(msg.data == "start") {
setStartState();
@ -2251,6 +2288,14 @@ $(document).ready(function(){
} else if(msg.cmd == "runs_remotely") {
remote = true;
hide([button_savetofile, button_import, button_importwi]);
} else if(msg.cmd == "debug_info") {
$("#debuginfo").val(msg.data);
} else if(msg.cmd == "set_debug") {
if(msg.data) {
debug_area.removeClass("hidden");
} else {
debug_area.addClass("hidden");
}
}
});
@ -2330,6 +2375,12 @@ $(document).ready(function(){
hidegenseqs();
});
button_actfwd.on("click", function(ev) {
hideMessage();
//hidegenseqs();
socket.send({'cmd': 'redo', 'data': ''});
});
button_actmem.on("click", function(ev) {
socket.send({'cmd': 'memory', 'data': ''});
});

3
static/pin.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="8" height="8" data-icon="pin" viewBox="0 0 8 8">
<path d="M1.344 0a.502.502 0 0 0 .156 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.438 1 .563-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.094 0 .502.502 0 0 0-.063 0z"/>
</svg>

After

Width:  |  Height:  |  Size: 295 B

View File

@ -123,6 +123,7 @@
<button type="button" class="btn btn-primary" id="btn_actmem">Memory</button>
<button type="button" class="btn btn-primary" id="btn_actwi">W Info</button>
<button type="button" class="btn btn-primary" id="btn_actundo">Back</button>
<button type="button" class="btn btn-primary" id="btn_actredo">Redo</button>
<button type="button" class="btn btn-primary" id="btn_actretry">Retry</button>
</div>
<input type="text" id="chatname" class="form-control hidden" placeholder="Chat name">
@ -185,6 +186,9 @@
</div>
</div>
</div>
<div class="hidden" id="debugcontainer">
<textarea class="form-control" placeholder="Debug Info" id="debuginfo"></textarea>
</div>
</div>
</div>
<div class="hidden" id="popupcontainer">
@ -228,7 +232,9 @@
<div class="popuptitletext">Enter Name For Save</div>
</div>
<div class="aidgpopupcontent">
<input class="form-control" type="text" placeholder="Save Name" id="savename">
<input class="form-control" type="text" placeholder="Save Name" id="savename"><br>
<input type="checkbox" data-toggle="toggle" data-onstyle="success" id="savepins" checked>
<div class="box-label">Save Pin Information</div>
</div>
<div class="popuperror hidden">
<span></span>