diff --git a/aiserver.py b/aiserver.py
index 4c40ce9c..96249ce7 100644
--- a/aiserver.py
+++ b/aiserver.py
@@ -4233,14 +4233,17 @@ def apiactionsubmit(data, use_memory=False, use_world_info=False, use_story=Fals
mem = koboldai_vars.memory + "\n"
else:
mem = koboldai_vars.memory
+
if(use_authors_note and koboldai_vars.authornote != ""):
anotetxt = ("\n" + koboldai_vars.authornotetemplate + "\n").replace("<|>", koboldai_vars.authornote)
else:
anotetxt = ""
+
MIN_STORY_TOKENS = 8
story_tokens = []
mem_tokens = []
wi_tokens = []
+
story_budget = lambda: koboldai_vars.max_length - koboldai_vars.sp_length - koboldai_vars.genamt - len(tokenizer._koboldai_header) - len(story_tokens) - len(mem_tokens) - len(wi_tokens)
budget = lambda: story_budget() + MIN_STORY_TOKENS
if budget() < 0:
@@ -4248,15 +4251,20 @@ def apiactionsubmit(data, use_memory=False, use_world_info=False, use_story=Fals
"msg": f"Your Max Tokens setting is too low for your current soft prompt and tokenizer to handle. It needs to be at least {koboldai_vars.max_length - budget()}.",
"type": "token_overflow",
}}), mimetype="application/json", status=500))
+
if use_memory:
mem_tokens = tokenizer.encode(utils.encodenewlines(mem))[-budget():]
+
if use_world_info:
world_info, _ = checkworldinfo(data, force_use_txt=True, scan_story=use_story)
wi_tokens = tokenizer.encode(utils.encodenewlines(world_info))[-budget():]
+
if use_story:
if koboldai_vars.useprompt:
story_tokens = tokenizer.encode(utils.encodenewlines(koboldai_vars.prompt))[-budget():]
+
story_tokens = tokenizer.encode(utils.encodenewlines(data))[-story_budget():] + story_tokens
+
if use_story:
for i, action in enumerate(reversed(koboldai_vars.actions.values())):
if story_budget() <= 0:
@@ -4267,6 +4275,7 @@ def apiactionsubmit(data, use_memory=False, use_world_info=False, use_story=Fals
story_tokens = tokenizer.encode(utils.encodenewlines(anotetxt))[-story_budget():] + story_tokens
if not koboldai_vars.useprompt:
story_tokens = tokenizer.encode(utils.encodenewlines(koboldai_vars.prompt))[-budget():] + story_tokens
+
tokens = tokenizer._koboldai_header + mem_tokens + wi_tokens + story_tokens
assert story_budget() >= 0
minimum = len(tokens) + 1
@@ -4428,7 +4437,8 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions, submission=None,
budget -= tknlen
else:
count = budget * -1
- tokens = acttkns[count:] + tokens
+ truncated_action_tokens = acttkns[count:]
+ tokens = truncated_action_tokens + tokens
budget = 0
break
@@ -4450,6 +4460,7 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions, submission=None,
# Did we get to add the A.N.? If not, do it here
if(anotetxt != ""):
if((not anoteadded) or forceanote):
+ # header, mem, wi, anote, prompt, actions
tokens = (tokenizer._koboldai_header if koboldai_vars.model not in ("Colab", "API", "OAI") else []) + memtokens + witokens + anotetkns + prompttkns + tokens
else:
tokens = (tokenizer._koboldai_header if koboldai_vars.model not in ("Colab", "API", "OAI") else []) + memtokens + witokens + prompttkns + tokens
@@ -4460,6 +4471,7 @@ def calcsubmitbudget(actionlen, winfo, mem, anotetxt, actions, submission=None,
# Send completed bundle to generator
assert len(tokens) <= koboldai_vars.max_length - lnsp - koboldai_vars.genamt - budget_deduction
ln = len(tokens) + lnsp
+
return tokens, ln+1, ln+koboldai_vars.genamt
#==================================================================#
diff --git a/koboldai_settings.py b/koboldai_settings.py
index c3c497b2..44d9638e 100644
--- a/koboldai_settings.py
+++ b/koboldai_settings.py
@@ -98,6 +98,7 @@ class koboldai_vars(object):
self._model_settings.reset_for_model_load()
def calc_ai_text(self, submitted_text=""):
+ context = []
token_budget = self.max_length
used_world_info = []
if self.tokenizer is None:
@@ -105,19 +106,32 @@ class koboldai_vars(object):
else:
used_tokens = self.sp_length
text = ""
+
+ # TODO: We may want to replace the "text" variable with a list-type
+ # class of context blocks, the class having a __str__ function.
+ if self.sp:
+ context.append({"type": "soft_prompt", "text": f"<{self.sp_length} tokens of Soft Prompt.>"})
+ # Header is never used?
+ # if koboldai_vars.model not in ("Colab", "API", "OAI") and self.tokenizer._koboldai_header:
+ # context.append({"type": "header", "text": f"{len(self.tokenizer._koboldai_header})
self.worldinfo_v2.reset_used_in_game()
#Add memory
memory_length = self.max_memory_length if self.memory_length > self.max_memory_length else self.memory_length
+ memory_text = None
if memory_length+used_tokens <= token_budget:
if self.memory_length > self.max_memory_length:
if self.tokenizer is None:
- text = self.memory
+ memory_text = self.memory
else:
- text += self.tokenizer.decode(self.tokenizer.encode(self.memory)[-self.max_memory_length-1:])
+ memory_text = self.tokenizer.decode(self.tokenizer.encode(self.memory)[-self.max_memory_length-1:])
else:
- text += self.memory
+ memory_text = self.memory
+
+ context.append({"type": "memory", "text": memory_text})
+ if memory_text:
+ text += memory_text
#Add constant world info entries to memory
for wi in self.worldinfo_v2:
@@ -126,7 +140,9 @@ class koboldai_vars(object):
used_tokens+=wi['token_length']
used_world_info.append(wi['uid'])
self.worldinfo_v2.set_world_info_used(wi['uid'])
- text += wi['content']
+ wi_text = wi['content']
+ context.append({"type": "world_info", "text": wi_text})
+ text += wi_text
#Add prompt lenght/text if we're set to always use prompt
if self.useprompt:
@@ -151,15 +167,18 @@ class koboldai_vars(object):
if used_tokens+0 if 'token_length' not in wi else wi['token_length'] <= token_budget:
used_tokens+=wi['token_length']
used_world_info.append(wi['uid'])
- text += wi['content']
+ wi_text = wi['content']
+ context.append({"type": "world_info", "text": wi_text})
+ text += wi_text
self.worldinfo_v2.set_world_info_used(wi['uid'])
- if self.prompt_length > self.max_prompt_length:
- if self.tokenizer is None:
- text += self.prompt
- else:
- text += self.tokenizer.decode(self.tokenizer.encode(self.prompt)[-self.max_prompt_length-1:])
- else:
- text += self.prompt
+
+ prompt_text = self.prompt
+ if self.tokenizer and self.prompt_length > self.max_prompt_length:
+ if self.tokenizer:
+ prompt_text += self.tokenizer.decode(self.tokenizer.encode(self.prompt)[-self.max_prompt_length-1:])
+
+ text += prompt_text
+ context.append({"type": "prompt", "text": self.prompt})
self.prompt_in_ai = True
else:
self.prompt_in_ai = False
@@ -169,13 +188,18 @@ class koboldai_vars(object):
#Start going through the actions backwards, adding it to the text if it fits and look for world info entries
game_text = ""
+ game_context = []
+ authors_note_final = self.authornotetemplate.replace("<|>", self.authornote)
used_all_tokens = False
for i in range(len(self.actions)-1, -1, -1):
if len(self.actions) - i == self.andepth and self.authornote != "":
- game_text = "{}{}".format(self.authornotetemplate.replace("<|>", self.authornote), game_text)
+ game_text = "{}{}".format(authors_note_final, game_text)
+ game_context.insert(0, {"type": "authors_note", "text": authors_note_final})
if self.actions.actions[i]["Selected Text Length"]+used_tokens <= token_budget and not used_all_tokens:
used_tokens += self.actions.actions[i]["Selected Text Length"]
- game_text = "{}{}".format(self.actions.actions[i]["Selected Text"], game_text)
+ selected_text = self.actions.actions[i]["Selected Text"]
+ game_text = "{}{}".format(selected_text, game_text)
+ game_context.insert(0, {"type": "action", "text": selected_text})
self.actions.set_action_in_ai(i)
#Now we need to check for used world info entries
for wi in self.worldinfo_v2:
@@ -196,7 +220,9 @@ class koboldai_vars(object):
if used_tokens+0 if 'token_length' not in wi else wi['token_length'] <= token_budget:
used_tokens+=wi['token_length']
used_world_info.append(wi['uid'])
- game_text = "{}{}".format(wi['content'], game_text)
+ wi_text = wi["content"]
+ game_text = "{}{}".format(wi_text, game_text)
+ game_context.insert(0, {"type": "world_info", "text": wi_text})
self.worldinfo_v2.set_world_info_used(wi['uid'])
else:
self.actions.set_action_in_ai(i, used=False)
@@ -204,7 +230,8 @@ class koboldai_vars(object):
#if we don't have enough actions to get to author's note depth then we just add it right before the game text
if len(self.actions) < self.andepth and self.authornote != "":
- game_text = "{}{}".format(self.authornotetemplate.replace("<|>", self.authornote), game_text)
+ game_text = "{}{}".format(authors_note_final, game_text)
+ game_context.insert(0, {"type": "authors_note", "text": authors_note_final})
if not self.useprompt:
if self.prompt_length + used_tokens < token_budget:
@@ -228,18 +255,25 @@ class koboldai_vars(object):
if used_tokens+0 if 'token_length' not in wi else wi['token_length'] <= token_budget:
used_tokens+=wi['token_length']
used_world_info.append(wi['uid'])
- text += wi['content']
+ wi_text = wi["content"]
+ text += wi_text
+ context.append({"type": "world_info", "text": wi_text})
self.worldinfo_v2.set_world_info_used(wi['uid'])
self.prompt_in_ai = True
else:
self.prompt_in_ai = False
text += self.prompt
+ context.append({"type": "prompt", "text": self.prompt})
text += game_text
+ context += game_context
+
if self.tokenizer is None:
tokens = []
else:
tokens = self.tokenizer.encode(text)
+
+ self.context = context
return tokens, used_tokens, used_tokens+self.genamt
def __setattr__(self, name, value):
@@ -444,7 +478,7 @@ class model_settings(settings):
class story_settings(settings):
local_only_variables = ['socketio', 'tokenizer', 'koboldai_vars']
- no_save_variables = ['socketio', 'tokenizer', 'koboldai_vars']
+ no_save_variables = ['socketio', 'tokenizer', 'koboldai_vars', 'context']
settings_name = "story"
def __init__(self, socketio, koboldai_vars, tokenizer=None):
self.socketio = socketio
@@ -503,6 +537,7 @@ class story_settings(settings):
self.max_prompt_length = 512
self.max_authornote_length = 512
self.prompt_in_ai = False
+ self.context = []
def save_story(self):
print("Saving")
diff --git a/static/koboldai.css b/static/koboldai.css
index e12d25c6..fe784ae9 100644
--- a/static/koboldai.css
+++ b/static/koboldai.css
@@ -804,6 +804,7 @@ td.server_vars {
padding-right: 35px;
margin-bottom: 10px;
flex-shrink: 0;
+ cursor: pointer;
}
.token_breakdown div {
@@ -1555,6 +1556,87 @@ body {
.model_setting_item_input {
width:95%;
}
+/*------------------------------ Context Viewer --------------------------------------------*/
+#context-viewer-container {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ background-color: rgba(0, 0, 0, 0.7);
+
+ width: 100vw;
+ height: 100vh;
+
+ z-index: 20;
+}
+
+#context-viewer {
+ display: flex;
+ flex-direction: column;
+
+ width: 50%;
+ height: 75%;
+ padding-bottom: 10px;
+ background-color: var(--layer1_palette);
+}
+
+#context-viewer-header {
+ display: flex;
+ justify-content: space-between;
+
+ padding: 5px;
+
+ background-color: var(--background);
+ margin-bottom: 3px;
+}
+
+#context-viewer-header-right {
+ display: flex;
+ flex-direction: row;
+}
+
+#context-viewer-close {
+ cursor: pointer;
+ float: right;
+}
+
+#context-viewer-header > h3 {
+ margin: 0px;
+ margin-top: 3px;
+}
+
+#context-container {
+ overflow-y: auto;
+ height: 100%;
+ flex-grow: 1;
+ padding: 0px 10px;
+}
+
+.context-symbol {
+ font-size: 1em !important;
+ position: relative;
+ top: 3px;
+ opacity: 0.5;
+}
+
+.context-block, .context-block-example{
+ margin: 0px 2px;
+}
+
+.context-block:hover {
+ outline: 1px solid gray;
+}
+
+.context-sp {background-color: var(--context_colors_soft_prompt);}
+.context-prompt {background-color: var(--context_colors_prompt);}
+.context-wi {background-color: var(--context_colors_world_info);}
+.context-memory {background-color: var(--context_colors_memory);}
+.context-an {background-color: var(--context_colors_authors_notes);}
+.context-action {background-color: var(--context_colors_game_text);}
/*---------------------------------- Global ------------------------------------------------*/
.hidden {
@@ -1719,7 +1801,11 @@ h2 .material-icons-outlined {
cursor: pointer;
}
-.material-icons-outlined, .collapsable_header, .section_header, .help_text {
+.material-icons-outlined,
+.collapsable_header,
+.section_header,
+.help_text,
+.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
diff --git a/static/koboldai.js b/static/koboldai.js
index 79bf5507..f974bc6d 100644
--- a/static/koboldai.js
+++ b/static/koboldai.js
@@ -470,6 +470,9 @@ function var_changed(data) {
option.setAttribute("title", sp[1][1]);
item.append(option);
}
+ //Special case for context viewer
+ } else if (data.classname == "story" && data.name == "context") {
+ update_context(data.value);
//Basic Data Syncing
} else {
var elements_to_change = document.getElementsByClassName("var_sync_"+data.classname.replace(" ", "_")+"_"+data.name.replace(" ", "_"));
@@ -2042,6 +2045,33 @@ function update_bias_slider_value(slider) {
slider.parentElement.parentElement.querySelector(".bias_slider_cur").textContent = slider.value;
}
+function update_context(data) {
+ $(".context-block").remove();
+
+ for (const entry of data) {
+ console.log(entry);
+ let contextClass = "context-" + ({
+ soft_prompt: "sp",
+ prompt: "prompt",
+ world_info: "wi",
+ memory: "memory",
+ authors_note: "an",
+ action: "action"
+ }[entry.type]);
+
+ let el = document.createElement("span");
+ el.classList.add("context-block");
+ el.classList.add(contextClass);
+ el.innerText = entry.text;
+
+ el.innerHTML = el.innerHTML.replaceAll("
", ' ');
+
+ document.getElementById("context-container").appendChild(el);
+ }
+
+
+}
+
function save_model_settings(settings = saved_settings) {
for (item of document.getElementsByClassName('setting_item_input')) {
if (item.id.includes("model")) {
@@ -2877,4 +2907,12 @@ $(document).ready(function(){
if (enabledTweaks.includes(path)) $(toggle).bootstrapToggle("on");
}
+
+ $("#context-viewer-close").click(function() {
+ document.getElementById("context-viewer-container").classList.add("hidden");
+ });
+
+ $(".token_breakdown").click(function() {
+ document.getElementById("context-viewer-container").classList.remove("hidden");
+ });
});
diff --git a/templates/index_new.html b/templates/index_new.html
index 0afad93e..f21e54f8 100644
--- a/templates/index_new.html
+++ b/templates/index_new.html
@@ -112,5 +112,32 @@
{% include 'templates.html' %}
+
+