mirror of
https://github.com/KoboldAI/KoboldAI-Client.git
synced 2025-06-05 21:59:24 +02:00
14
aiserver.py
14
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
|
||||
|
||||
#==================================================================#
|
||||
|
@@ -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")
|
||||
|
@@ -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;
|
||||
|
@@ -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("<br>", '<span class="material-icons-outlined context-symbol">keyboard_return</span>');
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
@@ -112,5 +112,32 @@
|
||||
{% include 'templates.html' %}
|
||||
</div>
|
||||
<iframe id="download_iframe" style="display:none;"></iframe>
|
||||
|
||||
<div id="context-viewer-container" class="hidden">
|
||||
<div id="context-viewer">
|
||||
<div id="context-viewer-header">
|
||||
<h3 class="noselect">Context Viewer</h3>
|
||||
<div id="context-viewer-header-right">
|
||||
<div>
|
||||
<span class="noselect">Key:</span>
|
||||
<div>
|
||||
<span class="noselect context-block-example context-sp">Soft Prompt</span>
|
||||
<span class="noselect context-block-example context-prompt">Prompt</span>
|
||||
<span class="noselect context-block-example context-wi">World Info</span>
|
||||
<span class="noselect context-block-example context-memory">Memory</span>
|
||||
<span class="noselect context-block-example context-an">Author's Note</span>
|
||||
<span class="noselect context-block-example context-action">Action</span>
|
||||
</div>
|
||||
</div>
|
||||
<span id="context-viewer-close" class="material-icons-outlined">close</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help_text">
|
||||
Context is the text the AI is sent when you ask it to generate text.
|
||||
As the context is limited in size, you can use the Context Viewer to check if things you want the AI to know are in the context.
|
||||
</span>
|
||||
<div id="context-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user