Merge pull request #416 from one-some/wi-fixes

(mostly) wi fixes and polish
This commit is contained in:
henk717
2023-07-31 20:52:48 +02:00
committed by GitHub
8 changed files with 278 additions and 206 deletions

View File

@@ -4396,8 +4396,8 @@ def requestwi():
# and items in different folders are sorted based on the order of the folders
#==================================================================#
def stablesortwi():
mapping = {uid: index for index, uid in enumerate(koboldai_vars.wifolders_l)}
koboldai_vars.worldinfo.sort(key=lambda x: mapping[str(x["folder"])] if x["folder"] is not None else float("inf"))
mapping = {int(uid): index for index, uid in enumerate(koboldai_vars.wifolders_l)}
koboldai_vars.worldinfo.sort(key=lambda x: mapping[int(x["folder"])] if x["folder"] is not None else float("inf"))
last_folder = ...
last_wi = None
for i, wi in enumerate(koboldai_vars.worldinfo):
@@ -6692,11 +6692,18 @@ def UI_2_set_wi_image(uid):
except FileNotFoundError:
pass
else:
# Otherwise assign image
with open(path, "wb") as file:
file.write(data)
try:
# Otherwise assign image
with open(path, "wb") as file:
file.write(data)
except FileNotFoundError:
show_error_notification(
"Unable to write image",
"Please save the game before uploading images."
)
return ":(", 500
koboldai_vars.gamesaved = False
return ":)"
return ":)", 200
@app.route("/get_wi_image/<int(signed=True):uid>", methods=["GET"])
@require_allowed_ip

View File

@@ -296,7 +296,7 @@ gensettingstf = [
"max": 1,
"step": 1,
"default": 0,
"tooltip": "Scans the AI's output for World Info keys as it is generating the one.",
"tooltip": "Look for World Info keys in the AI's response while it is still being generated.",
"menu_path": "World Info",
"sub_path": "",
"classname": "story",
@@ -756,7 +756,7 @@ gensettingstf = [
"max": 1,
"step": 1,
"default": 0,
"tooltip": "If enabled, experimental features will be displayed in the UI.",
"tooltip": "If enabled, experimental features will be displayed in the UI. Note: These features have been determined to be too unstable for standard use, and may corrupt your data. You're on your own from here.",
"menu_path": "Interface",
"sub_path": "UI",
"classname": "system",

View File

@@ -637,7 +637,12 @@ class InferenceModel:
)
time_end = round(time.time() - time_start, 2)
tokens_per_second = round(len(result.encoded[0]) / time_end, 2)
try:
tokens_per_second = round(len(result.encoded[0]) / time_end, 2)
except ZeroDivisionError:
# Introducing KoboldAI's fastest model: ReadOnly!
tokens_per_second = 0
if not utils.koboldai_vars.quiet:
logger.info(

View File

@@ -1,12 +1,10 @@
from __future__ import annotations
import torch
import requests
import numpy as np
from typing import List, Optional, Union
import utils
from logger import logger
from modeling.inference_model import (
GenerationResult,
GenerationSettings,
@@ -15,29 +13,46 @@ from modeling.inference_model import (
)
model_backend_name = "Read Only"
model_backend_type = "Read Only" #This should be a generic name in case multiple model backends are compatible (think Hugging Face Custom and Basic Hugging Face)
model_backend_type = "Read Only" # This should be a generic name in case multiple model backends are compatible (think Hugging Face Custom and Basic Hugging Face)
class BasicAPIException(Exception):
"""To be used for errors when using the Basic API as an interface."""
class DummyHFTokenizerOut:
input_ids = np.array([[]])
class FacadeTokenizer:
def __init__(self):
self._koboldai_header = []
def decode(self, _input):
return ""
def encode(self, input_text):
return []
def __call__(self, *args, **kwargs) -> DummyHFTokenizerOut:
return DummyHFTokenizerOut()
class model_backend(InferenceModel):
def __init__(self) -> None:
super().__init__()
# Do not allow API to be served over the API
# Do not allow ReadOnly to be served over the API
self.capabilties = ModelCapabilities(api_host=False)
self.tokenizer = self._tokenizer()
self.tokenizer: FacadeTokenizer = None
self.model = None
self.model_name = "Read Only"
def is_valid(self, model_name, model_path, menu_path):
return model_name == "ReadOnly"
def get_requested_parameters(self, model_name, model_path, menu_path, parameters = {}):
def get_requested_parameters(
self, model_name, model_path, menu_path, parameters={}
):
requested_parameters = []
return requested_parameters
def set_input_parameters(self, parameters):
return
@@ -46,17 +61,9 @@ class model_backend(InferenceModel):
def _initialize_model(self):
return
class _tokenizer():
def __init__(self):
self._koboldai_header = []
def decode(self, _input):
return ""
def encode(self, input_text):
return []
def _load(self, save_model: bool = False, initial_load: bool = False) -> None:
self.tokenizer = self.tokenizer
self.tokenizer = FacadeTokenizer()
self.model = None
utils.koboldai_vars.noai = True
@@ -72,7 +79,7 @@ class model_backend(InferenceModel):
):
return GenerationResult(
model=self,
out_batches=np.array([]),
out_batches=np.array([[]]),
prompt=prompt_tokens,
is_whole_generation=True,
single_line=single_line,

View File

@@ -1259,17 +1259,46 @@ td.server_vars {
.world_info_label_container > .generate-button:hover { opacity: 1.0; }
.tag {
display: inline-block;
background-color: var(--wi_tag_color);
color: var(--wi_tag_text_color);
margin-right: 3px;
margin-top: 3px;
padding: 2px;
margin-right: 2px;
padding-left: 3px;
padding-right: 3px;
border-radius: var(--radius_wi_card);
border: solid;
border-color: var(--wi_tag_color);
}
.tag .tag_button {
cursor: pointer;
opacity: 0.4;
font-size: 16px;
position: relative;
}
.tag .delete_icon {
cursor: pointer;
top: 3px;
right: 3px;
}
.tag .add_icon {
top: 3px;
right: 3px;
}
.tag .tag_text {
display: inline-block;
outline: none;
position: relative;
right: 3px;
}
.placeholder_tag .tag_text:empty {
opacity: 0.4;
}
.oi[folder] {
@@ -2851,6 +2880,10 @@ body {
height: 100%;
}
#welcome_text a {
text-decoration: underline;
}
.welcome_text {
display: flex;
height: 100%;
@@ -3569,10 +3602,15 @@ h2 .material-icons-outlined {
}
.section_header {
font-weight: bold;
margin-left: 2px;
margin-bottom: 2px;
}
.story_category_area > * > label {
user-select: none
}
.help_text {
margin-left: 6px;
margin-bottom: 0.7em;

View File

@@ -23,7 +23,13 @@ socket.on('error_popup', function(data){error_popup(data);});
socket.on("world_info_entry", function(data){process_world_info_entry(data);});
socket.on("world_info_entry_used_in_game", function(data){world_info_entry_used_in_game(data);});
socket.on("world_info_folder", function(data){world_info_folder(data);});
socket.on("delete_new_world_info_entry", function(data){document.getElementById("world_info_-1").remove();});
socket.on("delete_new_world_info_entry", function(data) {
const card = $el("#world_info_-1");
// Prevent weird race condition/strange event call order where blur event
// fires before removal is finished on Chrome
card.removing = true
card.remove();
});
socket.on("delete_world_info_entry", function(data){document.getElementById("world_info_"+data).remove();});
socket.on("delete_world_info_folder", function(data){document.getElementById("world_info_folder_"+data).remove();});
socket.on("error", function(data){show_error_message(data);});
@@ -87,6 +93,7 @@ var initial_socketio_connection_occured = false;
var selected_model_data;
var supported_gen_modes = [];
var privacy_mode_enabled = false;
var attention_wanting_wi_bar = null;
var ai_busy = false;
var can_show_options = false;
@@ -1281,7 +1288,7 @@ function redrawPopup() {
delete_icon.setAttribute("tooltip", "Delete");
delete_icon.id = row.path;
delete_icon.setAttribute("folder", row.isFolder);
delete_icon.onclick = function () {
delete_icon.addEventListener("click", function(event) {
const message = this.getAttribute("folder") == "true" ? "Do you really want to delete this folder and ALL files under it?" : "Do you really want to delete this file?";
const delId = this.id;
@@ -1291,9 +1298,11 @@ function redrawPopup() {
denyText="I've changed my mind!",
confirmCallback=function() {
socket.emit("popup_delete", delId);
}
},
null,
event.shiftKey
);
};
});
}
icon_area.append(delete_icon);
tr.append(icon_area);
@@ -2203,6 +2212,7 @@ function world_info_entry(data) {
} else {
world_info_card.classList.remove("used_in_game");
}
const title = world_info_card.querySelector('.world_info_title');
title.id = "world_info_title_"+data.uid;
title.textContent = data.title;
@@ -2210,7 +2220,7 @@ function world_info_entry(data) {
title.setAttribute("original_text", data.title);
title.setAttribute("contenteditable", true);
title.classList.remove("pulse");
title.ondragstart=function() {event.preventDefault();event.stopPropagation();};
title.ondragstart=function(event) {event.preventDefault();event.stopPropagation();};
title.onblur = function () {
this.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
@@ -2220,20 +2230,31 @@ function world_info_entry(data) {
this.classList.add("pulse");
}
}
world_info_card.addEventListener('dragstart', dragStart);
world_info_card.addEventListener('dragend', dragend);
title.addEventListener("keydown", function(event) {
if (event.key === "Enter") {
event.preventDefault();
this.blur();
}
});
title.addEventListener('dragenter', dragEnter)
title.addEventListener('dragover', dragOver);
title.addEventListener('dragleave', dragLeave);
title.addEventListener('drop', drop);
delete_icon = world_info_card.querySelector('.world_info_delete');
world_info_card.addEventListener('dragstart', dragStart);
world_info_card.addEventListener('dragend', dragend);
const delete_icon = world_info_card.querySelector('.world_info_delete');
delete_icon.id = "world_info_delete_"+data.uid;
delete_icon.setAttribute("uid", data.uid);
delete_icon.setAttribute("wi-title", data.title);
delete_icon.onclick = function () {
delete_icon.addEventListener("click", function (event) {
const wiTitle = this.getAttribute("wi-title");
const wiUid = parseInt(this.getAttribute("uid"));
const wiElement = this.parentElement.parentElement;
deleteConfirmation([
{text: "You're about to delete World Info entry "},
{text: wiTitle, format: "bold"},
@@ -2247,9 +2268,11 @@ function world_info_entry(data) {
} else {
socket.emit("delete_world_info", wiUid);
}
}
},
null,
event.shiftKey
);
}
});
const wiImgContainer = world_info_card.querySelector(".world_info_image_container");
const wiImg = wiImgContainer.querySelector(".world_info_image");
@@ -2342,15 +2365,16 @@ function world_info_entry(data) {
this.classList.add("pulse");
})
tags = world_info_card.querySelector('.world_info_tag_primary_area');
const tags = world_info_card.querySelector('.world_info_tag_primary_area');
tags.id = "world_info_tags_"+data.uid;
//add tag content here
add_tags(tags, data);
add_tags(tags, data, "primary");
secondarytags = world_info_card.querySelector('.world_info_tag_secondary_area');
const secondarytags = world_info_card.querySelector('.world_info_tag_secondary_area');
secondarytags.id = "world_info_secondtags_"+data.uid;
//add second tag content here
add_secondary_tags(secondarytags, data);
add_tags(secondarytags, data, "secondary");
//w++ toggle
wpp_toggle_area = world_info_card.querySelector('.world_info_wpp_toggle_area');
wpp_toggle_area.id = "world_info_wpp_toggle_area_"+data.uid;
@@ -2723,8 +2747,9 @@ function world_info_folder(data) {
delete_button.classList.add("cursor");
delete_button.setAttribute("folder", folder_name);
delete_button.textContent = "delete";
delete_button.onclick = function () {
delete_button.addEventListener("click", function (event) {
const folderName = this.getAttribute("folder");
deleteConfirmation([
{text: "You're about to delete World Info folder "},
{text: folderName, format: "bold"},
@@ -2734,9 +2759,11 @@ function world_info_folder(data) {
],
confirmText="Go for it.",
denyText="I've changed my mind!",
confirmCallback=function() { socket.emit("delete_wi_folder", folderName); }
confirmCallback=function() { socket.emit("delete_wi_folder", folderName); },
null,
event.shiftKey
);
};
});
delete_button.classList.add("delete");
title.append(delete_button);
@@ -3241,6 +3268,8 @@ function upload_file_without_save(file_box) {
}
function send_world_info(uid) {
const cardEl = document.getElementById(`world_info_${uid}`);
if (cardEl.removing) return;
socket.emit("edit_world_info", world_info_data[uid]);
}
@@ -4443,161 +4472,139 @@ function removeA(arr) {
return arr;
}
function add_tags(tags, data) {
while (tags.firstChild) {
tags.removeChild(tags.firstChild);
function create_tag_element(tagText, uid, tagType) {
// tagText is string, or null for empty tag at end.
// barType should be "primary" or "secondary"
const isPlaceholderTag = tagText === null;
const wiCardEl = document.querySelector(`.world_info_card[uid="${uid}"]`)
const keyField = {primary: "key", secondary: "keysecondary"}[tagType];
const tagClassFragment = {primary: "tags", primary: "secondtags"}[tagType];
const tagEl = document.createElement("span");
tagEl.classList.add("tag");
if (isPlaceholderTag) tagEl.classList.add("placeholder_tag");
const xEl = document.createElement("span");
xEl.classList.add("material-icons-outlined");
xEl.classList.add("tag_button");
if (!isPlaceholderTag) {
xEl.classList.add("delete_icon");
xEl.textContent = "close";
} else {
xEl.classList.add("add_icon");
xEl.textContent = "add";
}
for (tag of data.key) {
tag_item = document.createElement("span");
tag_item.classList.add("tag");
x = document.createElement("span");
x.textContent = "x ";
x.classList.add("delete_icon");
x.setAttribute("uid", data.uid);
x.setAttribute("tag", tag);
x.onclick = function () {
removeA(world_info_data[this.getAttribute('uid')]['key'], this.getAttribute('tag'));
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
text = document.createElement("span");
text.textContent = tag;
text.setAttribute("contenteditable", true);
text.setAttribute("uid", data.uid);
text.setAttribute("tag", tag);
text.id = "world_info_tags_text_"+data.uid+"_"+tag;
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
for (var i = 0; i < world_info_data[this.getAttribute('uid')]['key'].length; i++) {
if (world_info_data[this.getAttribute('uid')]['key'][i] == this.getAttribute("tag")) {
world_info_data[this.getAttribute('uid')]['key'][i] = this.textContent;
}
}
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
tag_item.append(x);
tag_item.append(text);
tag_item.id = "world_info_tags_"+data.uid+"_"+tag;
tags.append(tag_item);
}
//add the blank tag
tag_item = document.createElement("span");
tag_item.classList.add("tag");
x = document.createElement("span");
x.textContent = "+ ";
tag_item.append(x);
text = document.createElement("span");
text.classList.add("rawtext");
text.textContent = " ";
text.setAttribute("uid", data.uid);
text.setAttribute("contenteditable", true);
text.id = "world_info_tags_text_"+data.uid+"_blank";
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
if (this.textContent.trim() != "") {
//console.log(this.textContent);
on_new_wi_item = this.id;
world_info_data[this.getAttribute('uid')]['key'].push(this.textContent);
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
} else {
this.textContent = " ";
}
};
text.onclick = function () {
this.textContent = "";
};
tag_item.append(text);
tag_item.id = "world_info_secondtags_"+data.uid+"_new";
tags.append(tag_item);
xEl.setAttribute("uid", uid);
xEl.setAttribute("tag", tagText);
xEl.addEventListener("click", function() {
removeA(
world_info_data[uid][keyField],
tagText
);
send_world_info(uid);
this.classList.add("pulse");
});
const textEl = document.createElement("span");
textEl.classList.add("tag_text");
textEl.textContent = tagText;
textEl.setAttribute("data-placeholder", "Tag")
textEl.setAttribute("contenteditable", true);
textEl.setAttribute("uid", uid);
textEl.setAttribute("tag", tagText);
textEl.setAttribute("draggable", "true");
textEl.id = `world_info_${tagClassFragment}_text_${uid}_${tagText || "blank"}`;
textEl.addEventListener("dragstart", function(event) {
event.preventDefault();
event.stopPropagation();
});
textEl.addEventListener("focus", function(event) {
wiCardEl.setAttribute('draggable', 'false');
this.setAttribute('draggable', 'false');
});
textEl.addEventListener("blur", function () {
wiCardEl.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
if (!isPlaceholderTag) {
// Normal tag
for (var i = 0; i < world_info_data[uid][keyField].length; i++) {
if (world_info_data[uid][keyField][i] !== tagText) {
world_info_data[uid][keyField][i] = this.innerText;
}
}
} else {
// Placeholder tag
if (!this.textContent.trim()) return;
on_new_wi_item = this.id;
world_info_data[uid][keyField].push(this.textContent);
}
send_world_info(uid);
this.classList.add("pulse");
});
textEl.addEventListener("keydown", function(event) {
if (event.key === "Enter") {
// Press Enter to save tag and focus next one
event.preventDefault();
// HACK: Work around the fact that the server is in control of
// placing these elements
attention_wanting_wi_bar = tagType;
// And don't wait for like 10000 years to randomly take focus from
// the user
setTimeout(() => attention_wanting_wi_bar = null, 500);
this.blur();
} else if (event.key === "Escape") {
}
})
tagEl.append(xEl);
tagEl.append(textEl);
tagEl.id = `world_info_${tagClassFragment}_${uid}_${tagText || "new"}`;
return tagEl;
}
function add_secondary_tags(tags, data) {
while (tags.firstChild) {
tags.removeChild(tags.firstChild);
function add_tags(tagBarEl, data, tagType) {
// tagType is either "primary" or "secondary"
// Remove existing tags
while (tagBarEl.firstChild) {
tagBarEl.removeChild(tagBarEl.firstChild);
}
for (tag of data.keysecondary) {
tag_item = document.createElement("span");
tag_item.classList.add("tag");
x = document.createElement("span");
x.textContent = "x ";
x.classList.add("delete_icon");
x.setAttribute("uid", data.uid);
x.setAttribute("tag", tag);
x.onclick = function () {
removeA(world_info_data[this.getAttribute('uid')]['keysecondary'], this.getAttribute('tag'));
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
text = document.createElement("span");
text.textContent = tag;
text.setAttribute("contenteditable", true);
text.setAttribute("uid", data.uid);
text.setAttribute("tag", tag);
text.id = "world_info_secondtags_text_"+data.uid+"_"+tag;
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
for (var i = 0; i < world_info_data[this.getAttribute('uid')]['keysecondary'].length; i++) {
if (world_info_data[this.getAttribute('uid')]['keysecondary'][i] == this.getAttribute("tag")) {
world_info_data[this.getAttribute('uid')]['keysecondary'][i] = this.textContent;
}
}
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
tag_item.append(x);
tag_item.append(text);
tag_item.id = "world_info_secondtags_"+data.uid+"_"+tag;
tags.append(tag_item);
const tagList = {
primary: data.key,
secondary: data.keysecondary
}[tagType];
for (tag of tagList) {
tagBarEl.append(create_tag_element(tag, data.uid, tagType));
}
//add the blank tag
tag_item = document.createElement("span");
tag_item.classList.add("tag");
x = document.createElement("span");
x.textContent = "+ ";
tag_item.append(x);
text = document.createElement("span");
text.classList.add("rawtext");
text.textContent = " ";
text.setAttribute("uid", data.uid);
text.setAttribute("contenteditable", true);
text.id = "world_info_secondtags_text_"+data.uid+"_blank";
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
if (this.textContent.trim() != "") {
on_new_wi_item = this.id;
world_info_data[this.getAttribute('uid')]['keysecondary'].push(this.textContent);
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
} else {
this.textContent = " ";
}
};
text.onclick = function () {
this.textContent = "";
};
tag_item.append(text);
tag_item.id = "world_info_secondtags_"+data.uid+"_new";
tags.append(tag_item);
const placeholderTagEl = create_tag_element(null, data.uid, tagType);
tagBarEl.append(placeholderTagEl);
if (attention_wanting_wi_bar === tagType) {
const textEl = placeholderTagEl.querySelector(".tag_text");
// HACK: Please don't ask because I do not know
setTimeout(() => textEl.focus(), 1);
}
}
function create_new_wi_entry(folder) {
var uid = -1;
for (item of document.getElementsByClassName('world_info_card')) {
@@ -6947,9 +6954,13 @@ function sFormatted2HTML(sFormatted) {
return outHTML;
}
function deleteConfirmation(sFormatted, confirmText, denyText, confirmCallback, denyCallback) {
function deleteConfirmation(sFormatted, confirmText, denyText, confirmCallback, denyCallback=null, bypass=false) {
if (bypass) {
confirmCallback();
return;
}
$el("#confirm-text").innerHTML = sFormatted2HTML(sFormatted);
$el("#confirm-confirm-button > .text").innerText = confirmText;
$el("#confirm-deny-button > .text").innerText = denyText;

View File

@@ -97,7 +97,7 @@
<div id="story_menu_wi" class="story_category_area tab-target tab-target-story hidden">
<h4 class="section_header" style="margin-left: 12px;">World Info</h4>
<div class="help_text" style="margin-left: 20px;">
Lore information, which the AI recalls by certain words.
Lore information, which the AI recalls with the mention of certain words.
<span class="helpicon material-icons-outlined" tooltip="Use this instead of Memory for information on things like characters, objects, events, places, and anything else with detail.">help_icon</span>
</div>
<div class="setting_tile_area wi_settings">

View File

@@ -22,12 +22,16 @@
<span
class="world_info_item_type"
contenteditable="true"
data-placeholder="Person"
data-placeholder="..."
spellcheck="false"
></span> <span class="helpicon material-icons-outlined" tooltip="Please enter a noun that describes a person, place or thing." "]">help_icon</span>
></span>
<span
class="helpicon material-icons-outlined"
tooltip='Please enter a noun that describes this entry. For example, "person", "weapon", or "building". This will be used with the Generate Content button below.'
>help_icon</span>
</div>
</div>
<span id="world_info_delete_" class="world_info_delete">X</span>
<span id="world_info_delete_" class="world_info_delete material-icons-outlined">close</span>
</div>
<div class="world_info_upper_container world_info_tag_area">