From 32a0d7c453df24e9bf829aa76a9b2e31ecca8731 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 12:49:28 -0500
Subject: [PATCH 1/8] More Lua API fixes
* Removed `vars.model_orig`
* `requirex()` in bridge.lua now maintains a separate module cache for each
userscript instead of using the same cache for all userscripts
* `vars.lua_deleted` and `vars.lua_edited` are now erased right before running
the input modifiers instead of right before each time the generation modifiers
are run
---
aiserver.py | 9 ++++-----
bridge.lua | 14 ++++++++------
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/aiserver.py b/aiserver.py
index 63d3aed5..a4f12997 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -85,7 +85,6 @@ class vars:
submission = "" # Same as above, but after applying input formatting
lastctx = "" # The last context submitted to the generator
model = "" # Model ID string chosen at startup
- model_orig = "" # Original model string before being changed by auto model type detection
model_type = "" # Model Type (Automatically taken from the model config)
noai = False # Runs the script without starting up the transformers pipeline
aibusy = False # Stops submissions while the AI is working
@@ -190,7 +189,7 @@ def getModelSelection():
while(vars.model == ''):
modelsel = input("Model #> ")
if(modelsel.isnumeric() and int(modelsel) > 0 and int(modelsel) <= len(modellist)):
- vars.model = vars.model_orig = modellist[int(modelsel)-1][1]
+ vars.model = modellist[int(modelsel)-1][1]
else:
print("{0}Please enter a valid selection.{1}".format(colors.RED, colors.END))
@@ -371,7 +370,7 @@ 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.")
args = parser.parse_args()
-vars.model = vars.model_orig = args.model;
+vars.model = args.model;
if args.remote:
vars.remote = True;
@@ -1448,6 +1447,8 @@ def lua_is_custommodel():
#==================================================================#
def execute_inmod():
vars.lua_logname = ...
+ vars.lua_edited = set()
+ vars.lua_deleted = set()
try:
tpool.execute(vars.lua_koboldbridge.execute_inmod)
except lupa.LuaError as e:
@@ -1461,8 +1462,6 @@ def execute_inmod():
set_aibusy(0)
def execute_genmod():
- vars.lua_edited = set()
- vars.lua_deleted = set()
vars.lua_koboldbridge.execute_genmod()
def execute_outmod():
diff --git a/bridge.lua b/bridge.lua
index 39acc222..01c54760 100644
--- a/bridge.lua
+++ b/bridge.lua
@@ -1038,7 +1038,7 @@ return function(_python, _bridged)
---@param t KoboldLib
---@return string
function KoboldLib_getters.model(t)
- return bridged.vars.model_orig
+ return bridged.vars.model
end
---@param t KoboldLib
@@ -1526,7 +1526,7 @@ return function(_python, _bridged)
end
local old_loadfile = loadfile
- local old_package_loaded = package.loaded
+ local package_loaded = {} ---@type table
local old_package_searchers = package.searchers
---@param modname string
---@param env table
@@ -1546,8 +1546,10 @@ return function(_python, _bridged)
return
end
local allowsearch = type(modname) == "string" and string.match(modname, "[^%w._-]") == nil and string.match(modname, "%.%.") == nil
- if allowsearch and old_package_loaded[modname] then
- return old_package_loaded[modname]
+ if allowsearch and package_loaded[env] == nil then
+ package_loaded[env] = {}
+ elseif allowsearch and package_loaded[env][modname] then
+ return package_loaded[env][modname]
end
local loader, path
local errors = {}
@@ -1568,8 +1570,8 @@ return function(_python, _bridged)
return
end
local retval = old_loadfile(path, "t", env)()
- old_package_loaded[modname] = retval == nil or retval
- return old_package_loaded[modname], path
+ package_loaded[env][modname] = retval == nil or retval
+ return package_loaded[env][modname], path
end
local function _safe_require(_g)
---@param modname string
From 6183ecd66911343ab9800178f5107ee374a7d317 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 14:23:56 -0500
Subject: [PATCH 2/8] Fix `io`-related security issues in Lua sandbox
* `io.lines` with a string as first argument is now disallowed because
it reads a file given a filename
* `io.input` and `io.output` no longer permit having a string as first
argument because that would allow access to local files
---
bridge.lua | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/bridge.lua b/bridge.lua
index 01c54760..8b95c99e 100644
--- a/bridge.lua
+++ b/bridge.lua
@@ -1581,6 +1581,36 @@ return function(_python, _bridged)
end
end
+ local old_input = io.input
+ ---@param file? string|file*
+ local function safe_input(file)
+ if type(file) == "string" then
+ error("Calling `io.input` with a string as argument is disabled for security reasons")
+ return
+ end
+ return old_input(file)
+ end
+
+ local old_output = io.output
+ ---@param file? string|file*
+ local function safe_output(file)
+ if type(file) == "string" then
+ error("Calling `io.output` with a string as argument is disabled for security reasons")
+ return
+ end
+ return old_output(file)
+ end
+
+ local old_lines = io.lines
+ ---@param filename? string
+ local function safe_lines(filename, ...)
+ if type(filename) == "string" then
+ error("Calling `io.lines` with a string as first argument is disabled for security reasons")
+ return
+ end
+ return old_lines(filename, ...)
+ end
+
local function redirected_print(...)
local args = table.pack(...)
for i = 1, args.n do
@@ -1711,12 +1741,12 @@ return function(_python, _bridged)
stdin = io.stdin,
stdout = io.stdout,
stderr = io.stderr,
- input = io.input,
- output = io.output,
+ input = safe_input,
+ output = safe_output,
read = io.read,
write = io.write,
close = _new_close(io.close),
- lines = io.lines,
+ lines = safe_lines,
flush = io.flush,
type = io.type,
},
From 8742453f952a0600c29af31d3b22e036f422455e Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 18:29:54 -0500
Subject: [PATCH 3/8] Add safeguards for token budget and text formatting
* Error messages are now shown when memory, author's note, etc. exceeds
budget by itself
* Formatting options no longer break if there are empty chunks in the
story (although there shouldn't be any in the first place)
* Number of generated tokens is now kept track of from Python
---
aiserver.py | 116 ++++++++++++++++++++++++++++-----------------
tpu_mtj_backend.py | 4 +-
utils.py | 12 +++--
3 files changed, 82 insertions(+), 50 deletions(-)
diff --git a/aiserver.py b/aiserver.py
index a4f12997..584f81ae 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -119,6 +119,7 @@ class vars:
lua_running = False # Whether or not Lua is running (i.e. wasn't stopped due to an error)
lua_edited = set() # Set of chunk numbers that were edited from a Lua generation modifier
lua_deleted = set() # Set of chunk numbers that were deleted from a Lua generation modifier
+ generated_tkns = 0 # If using a backend that supports Lua generation modifiers, how many tokens have already been generated, otherwise 0
spfilename = "" # Filename of soft prompt to load, or an empty string if not using a soft prompt
userscripts = [] # List of userscripts to load
last_userscripts = [] # List of previous userscript filenames from the previous time userscripts were send via usstatitems
@@ -796,7 +797,10 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
scores: torch.FloatTensor,
**kwargs,
) -> bool:
- if(vars.lua_koboldbridge.generated_cols >= vars.genamt):
+ vars.generated_tkns += 1
+ if(vars.lua_koboldbridge.generated_cols and vars.generated_tkns != vars.lua_koboldbridge.generated_cols):
+ raise RuntimeError(f"Inconsistency detected between KoboldAI Python and Lua backends ({vars.generated_tkns} != {vars.lua_koboldbridge.generated_cols})")
+ if(vars.generated_tkns >= vars.genamt):
self.regeneration_required = False
self.halt = False
return True
@@ -808,7 +812,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
vars.lua_koboldbridge.regeneration_required = False
for i in range(vars.numseqs):
- vars.lua_koboldbridge.generated[i+1][vars.lua_koboldbridge.generated_cols] = input_ids[i, -1].item()
+ vars.lua_koboldbridge.generated[i+1][vars.generated_tkns] = int(input_ids[i, -1].item())
if(not vars.dynamicscan):
return self.regeneration_required or self.halt
@@ -1145,7 +1149,7 @@ def lua_compute_context(submission, entries, folders):
i += 1
winfo, mem, anotetxt, _ = calcsubmitbudgetheader(submission, allowed_entries=allowed_entries, allowed_folders=allowed_folders, force_use_txt=True)
txt, _, _ = calcsubmitbudget(len(actions), winfo, mem, anotetxt, actions)
- return txt
+ return tokenizer.decode(txt)
#==================================================================#
# Get property of a world info entry given its UID and property name
@@ -2241,38 +2245,53 @@ def calcsubmitbudgetheader(txt, **kwargs):
return winfo, mem, anotetxt, found_entries
-def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions):
+def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions, submission=None, budget_deduction=0):
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
anotetkns = [] # Placeholder for Author's Note tokens
lnanote = 0 # Placeholder for Author's Note length
- # Calculate token budget
- prompttkns = tokenizer.encode(vars.comregex_ai.sub('', vars.prompt), max_length=1+int(vars.max_length), truncation=True)
- lnprompt = len(prompttkns)
-
- memtokens = tokenizer.encode(mem, max_length=1+int(vars.max_length), truncation=True)
- lnmem = len(memtokens)
-
- witokens = tokenizer.encode(winfo, max_length=1+int(vars.max_length), truncation=True)
- lnwi = len(witokens)
-
- if(anotetxt != ""):
- anotetkns = tokenizer.encode(anotetxt, max_length=1+int(vars.max_length), truncation=True)
- lnanote = len(anotetkns)
-
lnsp = vars.sp.shape[0] if vars.sp is not None else 0
-
+
+ # Calculate token budget
+ prompttkns = tokenizer.encode(vars.comregex_ai.sub('', vars.prompt), max_length=int(2e9), truncation=True)
+ lnprompt = len(prompttkns)
+
+ memtokens = tokenizer.encode(mem, max_length=int(2e9), truncation=True)
+ lnmem = len(memtokens)
+ if(lnmem > vars.max_length - lnsp - vars.genamt - budget_deduction):
+ raise OverflowError("The memory in your story is too long. Please either write a shorter memory text or increase the Max Tokens setting. If you are using a soft prompt, additionally consider using a smaller soft prompt.")
+
+ witokens = tokenizer.encode(winfo, max_length=int(2e9), truncation=True)
+ lnwi = len(witokens)
+ if(lnmem + lnwi > vars.max_length - lnsp - vars.genamt - budget_deduction):
+ raise OverflowError("The current active world info keys take up too many tokens. Please either write shorter world info, decrease World Info Depth or increase the Max Tokens setting. If you are using a soft prompt, additionally consider using a smaller soft prompt.")
+
+ if(anotetxt != ""):
+ anotetkns = tokenizer.encode(anotetxt, max_length=int(2e9), truncation=True)
+ lnanote = len(anotetkns)
+ if(lnmem + lnwi + lnanote > vars.max_length - lnsp - vars.genamt - budget_deduction):
+ raise OverflowError("The author's note in your story is too long. Please either write a shorter author's note or increase the Max Tokens setting. If you are using a soft prompt, additionally consider using a smaller soft prompt.")
+
if(vars.useprompt):
- budget = vars.max_length - lnsp - lnprompt - lnmem - lnanote - lnwi - vars.genamt
+ budget = vars.max_length - lnsp - lnprompt - lnmem - lnanote - lnwi - vars.genamt - budget_deduction
else:
- budget = vars.max_length - lnsp - lnmem - lnanote - lnwi - vars.genamt
+ budget = vars.max_length - lnsp - lnmem - lnanote - lnwi - vars.genamt - budget_deduction
+
+ lnsubmission = len(tokenizer.encode(vars.comregex_ai.sub('', submission), max_length=int(2e9), truncation=True)) if submission is not None else 0
+ maybe_lnprompt = lnprompt if vars.useprompt and actionlen > 0 else 0
+
+ if(lnmem + lnwi + lnanote + maybe_lnprompt + lnsubmission > vars.max_length - lnsp - vars.genamt - budget_deduction):
+ raise OverflowError("Your submission is too long. Please either write a shorter submission or increase the Max Tokens setting. If you are using a soft prompt, additionally consider using a smaller soft prompt. If you are using the Always Add Prompt setting, turning it off may help.")
+
+ assert budget >= 0
if(actionlen == 0):
# First/Prompt action
- subtxt = vars.memory + winfo + anotetxt + vars.comregex_ai.sub('', vars.prompt)
- lnsub = lnsp + lnmem + lnwi + lnprompt + lnanote
- return subtxt, lnsub+1, lnsub+vars.genamt
+ tokens = memtokens + witokens + anotetkns + prompttkns
+ assert len(tokens) <= vars.max_length - lnsp - vars.genamt - budget_deduction
+ ln = len(tokens) + lnsp
+ return tokens, ln+1, ln+vars.genamt
else:
tokens = []
@@ -2285,9 +2304,10 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions):
for key in reversed(actions):
chunk = vars.comregex_ai.sub('', actions[key])
+ assert budget >= 0
if(budget <= 0):
break
- acttkns = tokenizer.encode(chunk, max_length=int(vars.max_length), truncation=True)
+ acttkns = tokenizer.encode(chunk, max_length=int(2e9), truncation=True)
tknlen = len(acttkns)
if(tknlen < budget):
tokens = acttkns + tokens
@@ -2312,7 +2332,7 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions):
prompttkns = prompttkns[-budget:]
else:
prompttkns = []
-
+
# Did we get to add the A.N.? If not, do it here
if(anotetxt != ""):
if((not anoteadded) or forceanote):
@@ -2322,10 +2342,11 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions):
else:
# Prepend Memory, WI, and Prompt before action tokens
tokens = memtokens + witokens + prompttkns + tokens
-
+
# Send completed bundle to generator
+ assert len(tokens) <= vars.max_length - lnsp - vars.genamt - budget_deduction
ln = len(tokens) + lnsp
- return tokenizer.decode(tokens), ln+1, ln+vars.genamt
+ return tokens, ln+1, ln+vars.genamt
#==================================================================#
# Take submitted text and build the text to be given to generator
@@ -2340,23 +2361,23 @@ def calcsubmit(txt):
# For all transformers models
if(vars.model != "InferKit"):
- subtxt, min, max = calcsubmitbudget(actionlen, winfo, mem, anotetxt, vars.actions)
+ subtxt, min, max = calcsubmitbudget(actionlen, winfo, mem, anotetxt, vars.actions, submission=txt)
if(actionlen == 0):
if(not vars.model in ["Colab", "OAI", "TPUMeshTransformerGPTJ"]):
generate(subtxt, min, max, found_entries=found_entries)
elif(vars.model == "Colab"):
- sendtocolab(subtxt, min, max)
+ sendtocolab(tokenizer.decode(subtxt), min, max)
elif(vars.model == "OAI"):
- oairequest(subtxt, min, max)
+ oairequest(tokenizer.decode(subtxt), min, max)
elif(vars.model == "TPUMeshTransformerGPTJ"):
tpumtjgenerate(subtxt, min, max, found_entries=found_entries)
else:
if(not vars.model in ["Colab", "OAI", "TPUMeshTransformerGPTJ"]):
generate(subtxt, min, max, found_entries=found_entries)
elif(vars.model == "Colab"):
- sendtocolab(subtxt, min, max)
+ sendtocolab(tokenizer.decode(subtxt), min, max)
elif(vars.model == "OAI"):
- oairequest(subtxt, min, max)
+ oairequest(tokenizer.decode(subtxt), min, max)
elif(vars.model == "TPUMeshTransformerGPTJ"):
tpumtjgenerate(subtxt, min, max, found_entries=found_entries)
@@ -2421,13 +2442,14 @@ def calcsubmit(txt):
#==================================================================#
def _generate(txt, minimum, maximum, found_entries):
- gen_in = tokenizer.encode(txt, return_tensors="pt", max_length=int(vars.max_length), truncation=True).long()
+ gen_in = torch.tensor(txt, dtype=torch.long)[None]
if(vars.sp is not None):
soft_tokens = torch.arange(
model.config.vocab_size,
model.config.vocab_size + vars.sp.shape[0],
)
gen_in = torch.cat((soft_tokens[None], gen_in), dim=-1)
+ assert gen_in.shape[-1] + vars.genamt <= vars.max_length
if(vars.hascuda and vars.usegpu):
gen_in = gen_in.to(vars.gpu_device)
@@ -2459,11 +2481,14 @@ def _generate(txt, minimum, maximum, found_entries):
num_return_sequences=numseqs
)
already_generated += len(genout[0]) - len(gen_in[0])
+ assert already_generated <= vars.genamt
if(model.kai_scanner.halt or not model.kai_scanner.regeneration_required):
break
assert genout.ndim >= 2
assert genout.shape[0] == vars.numseqs
- if(already_generated != vars.lua_koboldbridge.generated_cols):
+ if(vars.lua_koboldbridge.generated_cols and vars.generated_tkns != vars.lua_koboldbridge.generated_cols):
+ raise RuntimeError("Inconsistency detected between KoboldAI Python and Lua backends")
+ if(already_generated != vars.generated_tkns):
raise RuntimeError("WI scanning error")
for r in range(vars.numseqs):
for c in range(already_generated):
@@ -2474,8 +2499,8 @@ def _generate(txt, minimum, maximum, found_entries):
txt = tokenizer.decode(genout[i, -already_generated:])
winfo, mem, anotetxt, _found_entries = calcsubmitbudgetheader(txt, force_use_txt=True)
found_entries[i].update(_found_entries)
- txt, _, _ = calcsubmitbudget(len(vars._actions), winfo, mem, anotetxt, vars._actions)
- encoded.append(tokenizer.encode(txt, return_tensors="pt", max_length=int(vars.max_length), truncation=True)[0].long().to(genout.device))
+ txt, _, _ = calcsubmitbudget(len(vars._actions), winfo, mem, anotetxt, vars._actions, submission=txt)
+ encoded.append(torch.tensor(txt, dtype=torch.long, device=genout.device))
max_length = len(max(encoded, key=len))
encoded = torch.stack(tuple(torch.nn.functional.pad(e, (max_length - len(e), 0), value=model.config.pad_token_id or model.config.eos_token_id) for e in encoded))
genout = torch.cat(
@@ -2492,6 +2517,7 @@ def _generate(txt, minimum, maximum, found_entries):
device=genout.device,
)
genout = torch.cat((soft_tokens.tile(vars.numseqs, 1), genout), dim=-1)
+ assert genout.shape[-1] + vars.genamt - already_generated <= vars.max_length
diff = genout.shape[-1] - gen_in.shape[-1]
minimum += diff
maximum += diff
@@ -2503,14 +2529,16 @@ def _generate(txt, minimum, maximum, found_entries):
def generate(txt, minimum, maximum, found_entries=None):
+ vars.generated_tkns = 0
+
if(found_entries is None):
found_entries = set()
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
- print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, txt, colors.END))
+ 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
- vars.lastctx = txt
+ vars.lastctx = tokenizer.decode(txt)
# Clear CUDA cache if using GPU
if(vars.hascuda and (vars.usegpu or vars.breakmodel)):
@@ -2536,7 +2564,7 @@ def generate(txt, minimum, maximum, found_entries=None):
return
for i in range(vars.numseqs):
- vars.lua_koboldbridge.generated[i+1][vars.lua_koboldbridge.generated_cols] = genout[i, -1].item()
+ vars.lua_koboldbridge.generated[i+1][vars.generated_tkns] = int(genout[i, -1].item())
vars.lua_koboldbridge.outputs[i+1] = tokenizer.decode(genout[i, -already_generated:])
execute_outmod()
@@ -2707,7 +2735,7 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
found_entries = set()
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
- print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, txt, colors.END))
+ print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, tokenizer.decode(txt), colors.END))
# Submit input text to generator
try:
@@ -2737,7 +2765,7 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
genout = tpool.execute(
tpu_mtj_backend.infer,
- txt,
+ np.uint32(txt),
gen_len = maximum-minimum+1,
temp=vars.temp,
top_p=vars.top_p,
@@ -2804,8 +2832,8 @@ def getnewcontent(txt):
return txt
# Tokenize the last context and the generated content
- ctxtokens = tokenizer.encode(vars.lastctx, max_length=1+int(vars.max_length), truncation=True)
- txttokens = tokenizer.encode(txt, max_length=1+int(vars.max_length), truncation=True)
+ ctxtokens = tokenizer.encode(vars.lastctx, max_length=int(2e9), truncation=True)
+ txttokens = tokenizer.encode(txt, max_length=int(2e9), truncation=True)
dif = (len(txttokens) - len(ctxtokens)) * -1
# Remove the context from the returned text
diff --git a/tpu_mtj_backend.py b/tpu_mtj_backend.py
index ffb7b3e3..845edd30 100644
--- a/tpu_mtj_backend.py
+++ b/tpu_mtj_backend.py
@@ -268,7 +268,7 @@ class PenalizingCausalTransformer(CausalTransformer):
def infer(
- context: str,
+ context: np.array,
top_p=0.9,
temp=0.5,
top_k=0,
@@ -281,7 +281,7 @@ def infer(
) -> List[str]:
maps.thread_resources.env = thread_resources_env
total_batch = 1
- tokens = np.uint32(tokenizer.encode(context, max_length=params["seq"] - (soft_tokens.shape[0] if soft_tokens is not None else 0), truncation=True))
+ tokens = context
if(soft_tokens is not None):
tokens = np.uint32(np.concatenate((soft_tokens, tokens)))
provided_ctx = tokens.shape[0]
diff --git a/utils.py b/utils.py
index 95480c96..31184ed7 100644
--- a/utils.py
+++ b/utils.py
@@ -73,13 +73,15 @@ def addsentencespacing(txt, vars):
# Get last character of last action
if(len(vars.actions) > 0):
if(len(vars.actions[vars.actions.get_last_key()]) > 0):
- lastchar = vars.actions[vars.actions.get_last_key()][-1]
+ action = vars.actions[vars.actions.get_last_key()]
+ lastchar = action[-1] if len(action) else ""
else:
# Last action is blank, this should never happen, but
# since it did let's bail out.
return txt
else:
- lastchar = vars.prompt[-1]
+ action = vars.prompt
+ lastchar = action[-1] if len(action) else ""
if(lastchar == "." or lastchar == "!" or lastchar == "?" or lastchar == "," or lastchar == ";" or lastchar == ":"):
txt = " " + txt
return txt
@@ -88,13 +90,15 @@ def singlelineprocessing(txt, vars):
txt = vars.regex_sl.sub('', txt)
if(len(vars.actions) > 0):
if(len(vars.actions[vars.actions.get_last_key()]) > 0):
- lastchar = vars.actions[vars.actions.get_last_key()][-1]
+ action = vars.actions[vars.actions.get_last_key()]
+ lastchar = action[-1] if len(action) else ""
else:
# Last action is blank, this should never happen, but
# since it did let's bail out.
return txt
else:
- lastchar = vars.prompt[-1]
+ action = vars.prompt
+ lastchar = action[-1] if len(action) else ""
if(lastchar != "\n"):
txt = txt + "\n"
return txt
From 6aa2f50045187bffbe624c18d6055664bdddbed3 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 19:07:44 -0500
Subject: [PATCH 4/8] Add `chatmode` and `chatname` fields in bridge.lua
---
bridge.lua | 2 ++
1 file changed, 2 insertions(+)
diff --git a/bridge.lua b/bridge.lua
index 8b95c99e..f9ce25fb 100644
--- a/bridge.lua
+++ b/bridge.lua
@@ -888,6 +888,8 @@ return function(_python, _bridged)
---@field rmspch boolean
---@field adsnsp boolean
---@field singleline boolean
+ ---@field chatmode boolean
+ ---@field chatname string
local KoboldSettings = setmetatable({
_name = "KoboldSettings",
}, metawrapper)
From 1ff563ebdacb065bdefcf10d51f49a40ddcf9d43 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 19:40:20 -0500
Subject: [PATCH 5/8] Fix random story generator when No Prompt Gen is enabled
---
aiserver.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/aiserver.py b/aiserver.py
index 584f81ae..7ed1a84c 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -2055,7 +2055,7 @@ def settingschanged():
#==================================================================#
# Take input text from SocketIO and decide what to do with it
#==================================================================#
-def actionsubmit(data, actionmode=0, force_submit=False):
+def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False):
# Ignore new submissions if the AI is currently busy
if(vars.aibusy):
return
@@ -2092,7 +2092,7 @@ def actionsubmit(data, actionmode=0, force_submit=False):
assert False
# Start the game
vars.gamestarted = True
- if(not vars.noai and vars.lua_koboldbridge.generating and not vars.nopromptgen):
+ if(not vars.noai and vars.lua_koboldbridge.generating and (not vars.nopromptgen or force_prompt_gen)):
# Save this first action as the prompt
vars.prompt = data
# Clear the startup text from game screen
@@ -4152,7 +4152,7 @@ def randomGameRequest(topic):
newGameRequest()
vars.memory = "You generate the following " + topic + " story concept :"
vars.lua_koboldbridge.feedback = None
- actionsubmit("", force_submit=True)
+ actionsubmit("", force_submit=True, force_prompt_gen=True)
vars.memory = ""
#==================================================================#
From 9288a3de2f01d4464bd40c0a39bed3b33c9d2973 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 19:52:56 -0500
Subject: [PATCH 6/8] Allow retry button to regenerate random story
---
aiserver.py | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/aiserver.py b/aiserver.py
index 7ed1a84c..4488b706 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -154,6 +154,7 @@ class vars:
saveow = False # Whether or not overwrite confirm has been displayed
genseqs = [] # Temporary storage for generated sequences
recentback = False # Whether Back button was recently used without Submitting or Retrying after
+ recentrng = None # If a new random game was recently generated without Submitting after, this is the topic used (as a string), otherwise this is None
useprompt = False # 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)
@@ -2055,7 +2056,7 @@ def settingschanged():
#==================================================================#
# Take input text from SocketIO and decide what to do with it
#==================================================================#
-def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False):
+def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False, disable_recentrng=False):
# Ignore new submissions if the AI is currently busy
if(vars.aibusy):
return
@@ -2063,6 +2064,9 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
while(True):
set_aibusy(1)
+ if(disable_recentrng):
+ vars.recentrng = None
+
vars.recentback = False
vars.recentedit = False
vars.actionmode = actionmode
@@ -2101,6 +2105,7 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
if(vars.lua_koboldbridge.restart_sequence is not None and len(vars.genseqs) == 0):
data = ""
force_submit = True
+ disable_recentrng = True
continue
emit('from_server', {'cmd': 'scrolldown', 'data': ''}, broadcast=True)
break
@@ -2121,6 +2126,7 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
refresh_story()
data = ""
force_submit = True
+ disable_recentrng = True
continue
else:
if(vars.lua_koboldbridge.restart_sequence is not None and vars.lua_koboldbridge.restart_sequence > 0):
@@ -2128,6 +2134,7 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
refresh_story()
data = ""
force_submit = True
+ disable_recentrng = True
continue
genselect(genout)
refresh_story()
@@ -2156,6 +2163,7 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
if(vars.lua_koboldbridge.restart_sequence is not None and len(vars.genseqs) == 0):
data = ""
force_submit = True
+ disable_recentrng = True
continue
emit('from_server', {'cmd': 'scrolldown', 'data': ''}, broadcast=True)
break
@@ -2173,12 +2181,14 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False)
if(vars.lua_koboldbridge.restart_sequence is not None):
data = ""
force_submit = True
+ disable_recentrng = True
continue
else:
if(vars.lua_koboldbridge.restart_sequence is not None and vars.lua_koboldbridge.restart_sequence > 0):
genresult(genout[vars.lua_koboldbridge.restart_sequence-1]["generated_text"])
data = ""
force_submit = True
+ disable_recentrng = True
continue
genselect(genout)
set_aibusy(0)
@@ -2194,6 +2204,9 @@ def actionretry(data):
return
if(vars.aibusy):
return
+ if(vars.recentrng is not None):
+ randomGameRequest(vars.recentrng)
+ return
# 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
@@ -2647,7 +2660,7 @@ def selectsequence(n):
vars.genseqs = []
if(vars.lua_koboldbridge.restart_sequence is not None):
- actionsubmit("", actionmode=vars.actionmode, force_submit=True)
+ actionsubmit("", actionmode=vars.actionmode, force_submit=True, disable_recentrng=True)
#==================================================================#
# Send transformers-style request to ngrok/colab host
@@ -4149,9 +4162,11 @@ def newGameRequest():
setStartState()
def randomGameRequest(topic):
+ vars.recentrng = topic
newGameRequest()
vars.memory = "You generate the following " + topic + " story concept :"
vars.lua_koboldbridge.feedback = None
+ vars.recentrng = None
actionsubmit("", force_submit=True, force_prompt_gen=True)
vars.memory = ""
From 1189781eac1c7dfa17fc1719cc5e7ba0d8c5c593 Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 22:21:58 -0500
Subject: [PATCH 7/8] Show a text box for chat name when Chat Mode is enabled
---
aiserver.py | 12 ++++++++++++
static/application.js | 21 +++++++++++++++++++--
static/custom.css | 7 +++++++
templates/index.html | 5 +++--
4 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/aiserver.py b/aiserver.py
index 4488b706..bc035fc0 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -1606,6 +1606,12 @@ def get_message(msg):
if(msg['cmd'] == 'submit'):
if(vars.mode == "play"):
vars.lua_koboldbridge.feedback = None
+ if(vars.chatmode):
+ if(type(msg['chatname']) is not str):
+ raise ValueError("Chatname must be a string")
+ vars.chatname = msg['chatname']
+ settingschanged()
+ emit('from_server', {'cmd': 'setchatname', 'data': vars.chatname}, broadcast=True)
actionsubmit(msg['data'], actionmode=msg['actionmode'])
elif(vars.mode == "edit"):
editsubmit(msg['data'])
@@ -1613,6 +1619,12 @@ def get_message(msg):
memsubmit(msg['data'])
# Retry Action
elif(msg['cmd'] == 'retry'):
+ if(vars.chatmode):
+ if(type(msg['chatname']) is not str):
+ raise ValueError("Chatname must be a string")
+ vars.chatname = msg['chatname']
+ settingschanged()
+ emit('from_server', {'cmd': 'setchatname', 'data': vars.chatname}, broadcast=True)
actionretry(msg['data'])
# Back/Undo Action
elif(msg['cmd'] == 'back'):
diff --git a/static/application.js b/static/application.js
index 620c2d02..560568e9 100644
--- a/static/application.js
+++ b/static/application.js
@@ -30,6 +30,7 @@ var button_actwi;
var game_text;
var input_text;
var message_text;
+var chat_name;
var settings_menu;
var format_menu;
var wi_menu;
@@ -722,6 +723,7 @@ function exitEditMode() {
function enterMemoryMode() {
memorymode = true;
setmodevisibility(false);
+ 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]);
@@ -732,6 +734,7 @@ function enterMemoryMode() {
function exitMemoryMode() {
memorymode = false;
setmodevisibility(adventure);
+ setchatnamevisibility(chatmode);
hideMessage();
button_actmem.html("Memory");
show([button_actback, button_actretry, button_actwi]);
@@ -744,6 +747,7 @@ 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]);
+ setchatnamevisibility(false);
show([wi_menu]);
disableSendBtn();
$("#gamescreen").addClass("wigamescreen");
@@ -753,6 +757,7 @@ function exitWiMode() {
hideMessage();
button_actwi.html("W Info");
hide([wi_menu]);
+ setchatnamevisibility(chatmode);
show([button_actback, button_actmem, button_actretry, game_text]);
enableSendBtn();
$("#gamescreen").removeClass("wigamescreen");
@@ -797,7 +802,7 @@ function dosubmit() {
input_text.val("");
hideMessage();
hidegenseqs();
- socket.send({'cmd': 'submit', 'actionmode': adventure ? action_mode : 0, 'data': txt});
+ socket.send({'cmd': 'submit', 'actionmode': adventure ? action_mode : 0, 'chatname': chatmode ? chat_name.val() : undefined, 'data': txt});
if(memorymode) {
memorytext = input_text.val();
}
@@ -1155,6 +1160,14 @@ function setmodevisibility(state) {
}
}
+function setchatnamevisibility(state) {
+ if(state){ // Enabling
+ show([chat_name]);
+ } else{ // Disabling
+ hide([chat_name]);
+ }
+}
+
function setadventure(state) {
adventure = state;
if(state) {
@@ -1169,6 +1182,7 @@ function setadventure(state) {
function setchatmode(state) {
chatmode = state;
+ setchatnamevisibility(state);
}
function autofocus(event) {
@@ -1706,6 +1720,7 @@ $(document).ready(function(){
game_text = $('#gametext');
input_text = $('#input_text');
message_text = $('#messagefield');
+ chat_name = $('#chatname');
settings_menu = $("#settingsmenu");
format_menu = $('#formatmenu');
anote_menu = $('#anoterowcontainer');
@@ -2130,6 +2145,8 @@ $(document).ready(function(){
} else if(msg.cmd == "hidegenseqs") {
// Collapse genseqs menu
hidegenseqs();
+ } else if(msg.cmd == "setchatname") {
+ chat_name.val(msg.data);
} else if(msg.cmd == "setlabelnumseq") {
// Update setting label with value from server
$("#setnumseqcur").html(msg.data);
@@ -2234,7 +2251,7 @@ $(document).ready(function(){
button_actretry.on("click", function(ev) {
hideMessage();
- socket.send({'cmd': 'retry', 'data': ''});
+ socket.send({'cmd': 'retry', 'chatname': chatmode ? chat_name.val() : undefined, 'data': ''});
hidegenseqs();
});
diff --git a/static/custom.css b/static/custom.css
index d17c0c1c..ee573bac 100644
--- a/static/custom.css
+++ b/static/custom.css
@@ -32,6 +32,13 @@ chunk.editing, chunk.editing * {
display: flex;
}
+#chatname {
+ background-color: #404040;
+ color: #ffffff;
+ width: 200px;
+ margin-left: 10px;
+}
+
#menuitems {
display: flex;
width: 100%;
diff --git a/templates/index.html b/templates/index.html
index fda8fd34..df53315d 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -10,12 +10,12 @@
-
+
-
+
@@ -124,6 +124,7 @@
+
From a4087b93e9d597695ae932c0eb30dcab13f81f3c Mon Sep 17 00:00:00 2001
From: Gnome Ann <>
Date: Sun, 26 Dec 2021 22:51:07 -0500
Subject: [PATCH 8/8] Fix random story retry, for real this time
---
aiserver.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aiserver.py b/aiserver.py
index bc035fc0..d9c575e6 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -1612,6 +1612,7 @@ def get_message(msg):
vars.chatname = msg['chatname']
settingschanged()
emit('from_server', {'cmd': 'setchatname', 'data': vars.chatname}, broadcast=True)
+ vars.recentrng = None
actionsubmit(msg['data'], actionmode=msg['actionmode'])
elif(vars.mode == "edit"):
editsubmit(msg['data'])
@@ -4178,7 +4179,6 @@ def randomGameRequest(topic):
newGameRequest()
vars.memory = "You generate the following " + topic + " story concept :"
vars.lua_koboldbridge.feedback = None
- vars.recentrng = None
actionsubmit("", force_submit=True, force_prompt_gen=True)
vars.memory = ""