Added support for Author's Note

Increased input textarea height
Removed generator options from save/load system
Set output length slider to use steps of 2
This commit is contained in:
KoboldAI Dev 2021-05-05 03:04:06 -04:00
parent 0ce77f4875
commit 229b10cb91
5 changed files with 254 additions and 73 deletions

View File

@ -40,20 +40,22 @@ modellist = [
# Variables
class vars:
lastact = "" # The last action submitted to the generator
model = ''
model = ""
noai = False # Runs the script without starting up the transformers pipeline
aibusy = False # Stops submissions while the AI is working
max_length = 500 # Maximum number of tokens to submit per action
genamt = 60 # Amount of text for each action to generate
rep_pen = 1.0 # Generator repetition_penalty
temp = 0.9 # Generator temperature
top_p = 1.0 # Generator top_p
rep_pen = 1.0 # Default generator repetition_penalty
temp = 0.9 # Default generator temperature
top_p = 1.0 # Default generator top_p
gamestarted = False
prompt = ""
memory = ""
authornote = ""
andepth = 3 # How far back in history to append author's note
actions = []
mode = "play" # Whether the interface is in play, memory, or edit mode
editln = 0 # Which line was last selected in Edit Mode
editln = 0 # Which line was last selected in Edit Mode
url = "https://api.inferkit.com/v1/models/standard/generate" # InferKit API URL
apikey = "" # API key to use for InferKit API calls
savedir = getcwd()+"\stories\\newstory.json"
@ -303,6 +305,13 @@ def get_message(msg):
elif(msg['cmd'] == 'setoutput'):
vars.genamt = int(msg['data'])
emit('from_server', {'cmd': 'setlabeloutput', 'data': msg['data']})
# 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']})
#==================================================================#
#
@ -333,9 +342,20 @@ def actionsubmit(data):
# Take submitted text and build the text to be given to generator
#==================================================================#
def calcsubmit(txt):
vars.lastact = txt # Store most recent action in memory (is this still needed?)
anotetxt = "" # Placeholder for Author's Note text
lnanote = 0 # Placeholder for Author's Note length
forceanote = False # In case we don't have enough actions to hit A.N. depth
anoteadded = False # In case our budget runs out before we hit A.N. depth
actionlen = len(vars.actions)
# Build Author's Note if set
if(vars.authornote != ""):
anotetxt = "\n[Author's note: "+vars.authornote+"]\n"
# For all transformers models
if(vars.model != "InferKit"):
vars.lastact = txt # Store most recent action in memory (is this still needed?)
anotetkns = [] # Placeholder for Author's Note tokens
# Calculate token budget
prompttkns = tokenizer.encode(vars.prompt)
@ -344,17 +364,28 @@ def calcsubmit(txt):
memtokens = tokenizer.encode(vars.memory)
lnmem = len(memtokens)
budget = vars.max_length - lnprompt - lnmem - vars.genamt
if(anotetxt != ""):
anotetkns = tokenizer.encode(anotetxt)
lnanote = len(anotetkns)
if(len(vars.actions) == 0):
budget = vars.max_length - lnprompt - lnmem - lnanote - vars.genamt
if(actionlen == 0):
# First/Prompt action
subtxt = vars.memory + vars.prompt
lnsub = len(memtokens+prompttkns)
subtxt = vars.memory + anotetxt + vars.prompt
lnsub = lnmem + lnprompt + lnanote
generate(subtxt, lnsub+1, lnsub+vars.genamt)
else:
tokens = []
# Check if we have the action depth to hit our A.N. depth
if(anotetxt != "" and actionlen < vars.andepth):
forceanote = True
# Get most recent action tokens up to our budget
tokens = []
for n in range(len(vars.actions)):
for n in range(actionlen):
if(budget <= 0):
break
acttkns = tokenizer.encode(vars.actions[(-1-n)])
@ -366,9 +397,22 @@ def calcsubmit(txt):
count = budget * -1
tokens = acttkns[count:] + tokens
break
# Add mmory & prompt tokens to beginning of bundle
tokens = memtokens + prompttkns + tokens
# Inject Author's Note if we've reached the desired depth
if(n == vars.andepth-1):
if(anotetxt != ""):
tokens = anotetkns + tokens # A.N. len already taken from bdgt
anoteadded = True
# Did we get to add the A.N.? If not, do it here
if(anotetxt != ""):
if((not anoteadded) or forceanote):
tokens = memtokens + anotetkns + prompttkns + tokens
else:
tokens = memtokens + prompttkns + tokens
else:
# Prepend Memory and Prompt before action tokens
tokens = memtokens + prompttkns + tokens
# Send completed bundle to generator
ln = len(tokens)
@ -379,9 +423,15 @@ def calcsubmit(txt):
)
# For InferKit web API
else:
budget = vars.max_length - len(vars.prompt) - len(vars.memory) - 1
# Check if we have the action depth to hit our A.N. depth
if(anotetxt != "" and actionlen < vars.andepth):
forceanote = True
budget = vars.max_length - len(vars.prompt) - len(anotetxt) - len(vars.memory) - 1
subtxt = ""
for n in range(len(vars.actions)):
for n in range(actionlen):
if(budget <= 0):
break
actlen = len(vars.actions[(-1-n)])
@ -392,12 +442,26 @@ def calcsubmit(txt):
count = budget * -1
subtxt = vars.actions[(-1-n)][count:] + subtxt
break
# Inject Author's Note if we've reached the desired depth
if(n == vars.andepth-1):
if(anotetxt != ""):
subtxt = anotetxt + subtxt # A.N. len already taken from bdgt
anoteadded = True
# Add mmory & prompt tokens to beginning of bundle
# Format memory for inclusion (adding newline separator)
memsub = ""
if(vars.memory != ""):
subtxt = vars.memory + "\n" + vars.prompt + subtxt
memsub = vars.memory + "\n"
# Did we get to add the A.N.? If not, do it here
if(anotetxt != ""):
if((not anoteadded) or forceanote):
subtxt = memsub + anotetxt + vars.prompt + subtxt
else:
subtxt = memsub + vars.prompt + subtxt
else:
subtxt = vars.prompt + subtxt
subtxt = memsub + vars.prompt + subtxt
# Send it!
ikrequest(subtxt)
@ -407,7 +471,12 @@ def calcsubmit(txt):
#==================================================================#
def generate(txt, min, max):
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.WARNING, min, max, txt, colors.ENDC))
# Clear CUDA cache if using GPU
if(vars.hascuda and vars.usegpu):
torch.cuda.empty_cache()
# Submit input text to generator
genout = generator(
txt,
do_sample=True,
@ -421,6 +490,10 @@ def generate(txt, min, max):
refresh_story()
emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)})
# Clear CUDA cache again if using GPU
if(vars.hascuda and vars.usegpu):
torch.cuda.empty_cache()
set_aibusy(0)
#==================================================================#
@ -460,6 +533,7 @@ def refresh_settings():
emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p})
emit('from_server', {'cmd': 'updatereppen', 'data': vars.rep_pen})
emit('from_server', {'cmd': 'updateoutlen', 'data': vars.genamt})
emit('from_server', {'cmd': 'updatanotedepth', 'data': vars.andepth})
#==================================================================#
# Sets the logical and display states for the AI Busy condition
@ -521,6 +595,7 @@ def togglememorymode():
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})
elif(vars.mode == "memory"):
vars.mode = "play"
emit('from_server', {'cmd': 'memmode', 'data': 'false'})
@ -534,6 +609,17 @@ def memsubmit(data):
vars.memory = data
vars.mode = "play"
emit('from_server', {'cmd': 'memmode', 'data': 'false'})
# Ask for contents of Author's Note field
emit('from_server', {'cmd': 'getanote', 'data': ''})
#==================================================================#
# Commit changes to Author's Note
#==================================================================#
def anotesubmit(data):
# Maybe check for length at some point
# For now just send it to storage
vars.authornote = data
#==================================================================#
# Assembles game data into a request to InferKit API
@ -611,11 +697,12 @@ def saveRequest():
js = {}
#js["maxlegth"] = vars.max_length # This causes problems when switching to/from InfraKit
#js["genamt"] = vars.genamt
js["rep_pen"] = vars.rep_pen
js["temp"] = vars.temp
#js["rep_pen"] = vars.rep_pen
#js["temp"] = vars.temp
js["gamestarted"] = vars.gamestarted
js["prompt"] = vars.prompt
js["memory"] = vars.memory
js["authorsnote"] = vars.authornote
js["actions"] = vars.actions
js["savedir"] = path
# Write it
@ -638,13 +725,18 @@ def loadRequest():
# Copy file contents to vars
#vars.max_length = js["maxlegth"] # This causes problems when switching to/from InfraKit
#vars.genamt = js["genamt"]
vars.rep_pen = js["rep_pen"]
vars.temp = js["temp"]
#vars.rep_pen = js["rep_pen"]
#vars.temp = js["temp"]
vars.gamestarted = js["gamestarted"]
vars.prompt = js["prompt"]
vars.memory = js["memory"]
vars.actions = js["actions"]
vars.savedir = js["savedir"]
# Try not to break older save files
if("authorsnote" in js):
vars.authornote = js["authorsnote"]
file.close()
# Refresh game screen
refresh_story()

View File

@ -1,8 +1,11 @@
//=================================================================//
// VARIABLES
//=================================================================//
// Socket IO Object
var socket;
// UI references for jQuery
var button_newgame;
var button_save;
var button_load;
@ -20,33 +23,46 @@ var setting_temp;
var setting_topp;
var setting_reppen;
var setting_outlen;
var label_temp;
var label_topp;
var label_reppen;
var label_outlen;
var anote_menu;
var anote_input;
var anote_labelcur;
var anote_slider;
var shift_down = false;
// Key states
var shift_down = false;
var do_clear_ent = false;
//=================================================================//
// METHODS
//=================================================================//
function enableButton(ref) {
ref.prop("disabled",false);
ref.removeClass("btn-secondary");
ref.addClass("btn-primary");
function enableButtons(refs) {
for(i=0; i<refs.length; i++) {
refs[i].prop("disabled",false);
refs[i].removeClass("btn-secondary");
refs[i].addClass("btn-primary");
}
}
function disableButton(ref) {
ref.prop("disabled",true);
ref.removeClass("btn-primary");
ref.addClass("btn-secondary");
function disableButtons(refs) {
for(i=0; i<refs.length; i++) {
refs[i].prop("disabled",true);
refs[i].removeClass("btn-primary");
refs[i].addClass("btn-secondary");
}
}
function enableSendBtn() {
enableButton(button_send)
enableButtons([button_send])
button_send.html("Submit");
}
function disableSendBtn() {
disableButton(button_send)
disableButtons([button_send])
button_send.html("");
}
@ -75,12 +91,16 @@ function hideWaitAnimation() {
$('#waitanim').remove();
}
function hide(ref) {
ref.addClass("hidden");
function hide(refs) {
for(i=0; i<refs.length; i++) {
refs[i].addClass("hidden");
}
}
function show(ref) {
ref.removeClass("hidden");
function show(refs) {
for(i=0; i<refs.length; i++) {
refs[i].removeClass("hidden");
}
}
function enterEditMode() {
@ -92,10 +112,8 @@ function enterEditMode() {
editModeSelect($(this).attr("n"));
});
disableSendBtn();
hide(button_actback);
hide(button_actmem);
hide(button_actretry);
show(button_delete);
hide([button_actback, button_actmem, button_actretry]);
show([button_delete]);
}
function exitEditMode() {
@ -105,10 +123,8 @@ function exitEditMode() {
game_text.children('chunk').removeClass("chunkhov");
game_text.off('click', '> *');
enableSendBtn();
show(button_actback);
show(button_actmem);
show(button_actretry);
hide(button_delete);
show([button_actback, button_actmem, button_actretry]);
hide([button_delete]);
input_text.val("");
}
@ -117,23 +133,20 @@ function editModeSelect(n) {
}
function enterMemoryMode() {
// Add class to each story chunk
showMessage("Edit the memory to be sent with each request to the AI.");
button_actmem.html("Cancel");
hide(button_actback);
hide(button_actretry);
hide(button_actedit);
hide(button_delete);
hide([button_actback, button_actretry, button_actedit, button_delete]);
// Display Author's Note field
anote_menu.slideDown("fast");
}
function exitMemoryMode() {
// Remove class to each story chunk
hideMessage();
button_actmem.html("Memory");
show(button_actback);
show(button_actretry);
show(button_actedit);
show([button_actback, button_actretry, button_actedit]);
input_text.val("");
// Hide Author's Note field
anote_menu.slideUp("fast");
}
function dosubmit() {
@ -160,7 +173,7 @@ function newTextHighlight(ref) {
$(document).ready(function(){
// Bind references
// Bind UI references
button_newgame = $('#btn_newgame');
button_save = $('#btn_save');
button_load = $('#btn_load');
@ -182,6 +195,10 @@ $(document).ready(function(){
label_topp = $('#settoppcur');
label_reppen = $('#setreppencur');
label_outlen = $('#setoutputcur');
anote_menu = $('#anoterowcontainer');
anote_input = $('#anoteinput');
anote_labelcur = $('#anotecur');
anote_slider = $('#anotedepth');
// Connect to SocketIO server
socket = io.connect('http://127.0.0.1:5000');
@ -203,24 +220,16 @@ $(document).ready(function(){
// Enable or Disable buttons
if(msg.data == "ready") {
enableSendBtn();
enableButton(button_actedit);
enableButton(button_actmem);
enableButton(button_actback);
enableButton(button_actretry);
enableButtons([button_actedit, button_actmem, button_actback, button_actretry]);
hideWaitAnimation();
} else if(msg.data == "wait") {
disableSendBtn();
disableButton(button_actedit);
disableButton(button_actmem);
disableButton(button_actback);
disableButton(button_actretry);
disableButtons([button_actedit, button_actmem, button_actback, button_actretry]);
showWaitAnimation();
} else if(msg.data == "start") {
enableSendBtn();
enableButton(button_actmem);
disableButton(button_actedit);
disableButton(button_actback);
disableButton(button_actretry);
enableButtons([button_actmem]);
disableButtons([button_actedit, button_actback, button_actretry]);
}
} else if(msg.cmd == "editmode") {
// Enable or Disable edit mode
@ -276,6 +285,20 @@ $(document).ready(function(){
} else if(msg.cmd == "setlabeloutput") {
// Update setting label with value from server
label_outlen.html(msg.data);
} else if(msg.cmd == "updatanotedepth") {
// 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);
}
});
@ -341,6 +364,7 @@ $(document).ready(function(){
}
});
// Enter to submit, but not if holding shift
input_text.keyup(function (ev) {
if (ev.which == 13 && do_clear_ent) {
input_text.val("");

View File

@ -85,7 +85,7 @@ chunk {
}
#input_text {
height: 60px;
height: 80px;
resize: none;
overflow:auto;
background-color: #404040;
@ -103,6 +103,38 @@ chunk {
left:5px;
}
#anoterowcontainer {
display: none;
}
#anoterow {
margin-top: 10px;
padding: 0px;
width: 100%;
display: grid;
grid-template-columns: 90% 10%;
}
#anoterowleft {
padding-right: 10px;
}
#anoteinput {
background-color: #404040;
color: #ffffff;
}
#anoteslidelabel {
color: #ffffff;
display: grid;
grid-template-columns: 80% 20%;
}
.anotelabel {
font-size: 10pt;
color: #ffffff;
}
.airange {
width: 100px;
}

View File

@ -1 +1 @@
{"rep_pen": 1.2, "temp": 1.0, "gamestarted": true, "prompt": "Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking", "memory": "Niko is a small red kobold.\nNiko has yellow, reptilian eyes and a long, scaly tail.\nNiko is hungry and looking to steal something to eat.", "actions": [" chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to", " do in a city? All that Niko needed to know was", " where to find the chicken and then how to make off with it.\n\nA soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,"], "savedir": ""}
{"gamestarted": true, "prompt": "Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking", "memory": "Niko is a small red kobold.\nNiko has yellow, reptilian eyes and a long, scaly tail.\nNiko is hungry and looking to steal something to eat.", "authorsnote": "Things are about to get crazy for poor Niko.", "actions": [" chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to", " do in a city? All that Niko needed to know was", " where to find the chicken and then how to make off with it.\n\nA soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,"], "savedir": ""}

View File

@ -102,7 +102,7 @@
</div>
</div>
<div>
<input type="range" class="form-range airange" min="10" max="500" step="1" id="setoutput">
<input type="range" class="form-range airange" min="10" max="500" step="2" id="setoutput">
</div>
<div class="settingminmax">
<div class="justifyleft">
@ -138,6 +138,39 @@
<button type="button" class="btn btn-secondary" id="btnsend">Wait</button>
</div>
</div>
<div id="anoterowcontainer">
<div id="anoterow">
<div id="anoterowleft">
<div class="anotelabel">
Author's Note
</div>
<div class="anotefield">
<input class="form-control" type="text" placeholder="Author's Note" id="anoteinput">
</div>
</div>
<div id="anoterowright">
<div id="anoteslidelabel">
<div class="justifyleft">
Depth
</div>
<div class="justifyright" id="anotecur">
3
</div>
</div>
<div>
<input type="range" class="form-range airange" min="1" max="5" step="1" id="anotedepth">
</div>
<div class="settingminmax">
<div class="justifyleft">
1
</div>
<div class="justifyright">
5
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>