Work on commentators

This commit is contained in:
somebody
2022-12-09 23:31:28 -06:00
parent 90e13f7f1f
commit d31ab1d648
6 changed files with 377 additions and 36 deletions

View File

@@ -7,6 +7,7 @@
# External packages
from dataclasses import dataclass
import random
import shutil
import eventlet
eventlet.monkey_patch(all=True, thread=False, os=False)
@@ -6261,6 +6262,8 @@ def generate(txt, minimum, maximum, found_entries=None):
gc.collect()
torch.cuda.empty_cache()
maybe_review_story()
set_aibusy(0)
#==================================================================#
@@ -9930,13 +9933,19 @@ def UI_2_refresh_auto_memory(data):
#==================================================================#
# Story review zero-shot
#==================================================================#
@socketio.on("story_review")
@logger.catch
def UI_2_story_review(data):
who = data["who"]
template = data.get("template", "\n\n%s's thoughts on this situation:\n\"")
prompt = template % who
logger.info(prompt)
def maybe_review_story():
if not (
koboldai_vars.commentary_characters
and koboldai_vars.commentary_chance
and koboldai_vars.commentary_enabled
):
return
if random.randrange(100) > koboldai_vars.commentary_chance:
return
speaker_id, speaker_name = random.choice(list(koboldai_vars.commentary_characters.items()))
prompt = "\n\n%s's thoughts on what just happened in this story: \"" % speaker_name
context = koboldai_vars.calc_ai_text(
prompt,
@@ -9949,10 +9958,8 @@ def UI_2_story_review(data):
max_new=30,
).decoded[0]
out_text = re.sub(r"[\s\(\)]", " ", out_text)
# Beware contractions!
# out_text = re.sub(r"[\s!\.\?]'", " ", out_text)
while " " in out_text:
out_text = out_text.replace(" ", " ")
@@ -9961,7 +9968,7 @@ def UI_2_story_review(data):
out_text = out_text.strip()
out_text = utils.trimincompletesentence(out_text)
emit("show_story_review", {"who": who, "review": out_text})
emit("show_story_review", {"who": speaker_name, "review": out_text, "id": speaker_id})
#==================================================================#
# Get next 100 actions for infinate scroll

View File

@@ -3,7 +3,7 @@ from dataclasses import dataclass
import importlib
import os, re, time, threading, json, pickle, base64, copy, tqdm, datetime, sys
import shutil
from typing import Union
from typing import List, Union
from io import BytesIO
from flask import has_request_context, session
from flask_socketio import SocketIO, join_room, leave_room
@@ -898,6 +898,12 @@ class story_settings(settings):
self.memory_attn_bias = 1
self.an_attn_bias = 1
self.chat_style = 0
# In percent!!!
self.commentary_chance = 0
# id: {name}
self.commentary_characters = {}
self.commentary_enabled = False
self.save_paths = SavePaths(os.path.join("stories", self.story_name or "Untitled"))
@@ -2443,6 +2449,10 @@ class SavePaths:
@property
def generated_images(self) -> str:
return os.path.join(self.base, "generated_images")
@property
def commentator_pictures(self) -> str:
return os.path.join(self.base, "commentator_pictures")
default_rand_range = [0.1, 1, 2]
default_creativity_range = [0.8, 1]

View File

@@ -183,6 +183,22 @@ border-top-right-radius: var(--tabs_rounding);
}
.dynamic-setting-container {
background-color: var(--setting_background);
color: var(--setting_text);
border-radius: var(--radius_settings_background);
padding: 2px;
margin-bottom: 5px;
width: 100%;
}
.dynamic-setting-bounds, .dynamic-setting-top {
width: 100%;
display: flex;
justify-content: space-between;
}
.setting_container {
display: grid;
@@ -788,7 +804,7 @@ border-top-right-radius: var(--tabs_rounding);
}
/* Tweaks */
.tweak-container {
.wide-boolean-setting {
display: flex;
align-items: center;
justify-content: space-between;
@@ -1188,10 +1204,11 @@ body {
margin-left: var(--setting_menu_closed_width_no_pins_width);
margin-right: var(--flyout_menu_closed_width);
grid-template-areas: "menuicon gamescreen options lefticon"
"menuicon review review lefticon"
"menuicon theme theme lefticon"
"menuicon inputrow inputrow lefticon";
grid-template-columns: 30px auto 30% 30px;
grid-template-rows: auto min-content 100px;
grid-template-rows: auto min-content min-content 100px;
}
.main-grid[option_length="0"][model_numseqs="1"] {
grid-template-columns: 30px auto 0px 30px;
@@ -2810,6 +2827,79 @@ body {
.genre-inner > .x:hover { opacity: 1; }
/* Story Commentary */
#story-review {
grid-area: review;
min-height: 52px;
padding: 10px;
background-color: var(--gamescreen_background);
margin: 10px 0px;
}
#story-review-img {
width: 48px;
height: 48px;
object-fit: cover;
float: left;
margin-right: 10px;
}
#story-review-author {
display: block;
font-weight: bold;
}
#story-commentary-settings { width: 100%; }
.story-commentary-character {
display: flex;
align-items: stretch;
margin-bottom: 5px;
}
.story-commentary-character > .image-container {
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--setting_background);
margin-right: 10px;
}
.story-commentary-character > .image-container > img {
width: 100%;
height: 100%;
object-fit: cover;
}
.story-commentary-character > .name {
border: none;
flex-grow: 1;
}
.story-commentary-character > .close {
float: right;
color: white !important;
transform: scale(1) !important;
display: flex;
align-items: center;
margin-left: 8px;
}
#story-commentary-settings > .add {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
transform: scale(1) !important;
margin-top: 3px;
}
#story-commentary-settings > p {
font-size: calc(1.1em + var(--font_size_adjustment));
}
/*---------------------------------- Global ------------------------------------------------*/
.hidden {
@@ -3045,6 +3135,7 @@ h2 .material-icons-outlined {
.help_text,
.sample_order,
.table-header-label,
.disabled,
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;

View File

@@ -34,7 +34,6 @@ socket.on("log_message", function(data){process_log_message(data);});
socket.on("debug_message", function(data){console.log(data);});
socket.on("scratchpad_response", recieveScratchpadResponse);
socket.on("show_error_notification", function(data) { reportError(data.title, data.text) });
socket.on("show_story_review", showStoryReview);
//socket.onAny(function(event_name, data) {console.log({"event": event_name, "class": data.classname, "data": data});});
// Must be done before any elements are made; we track their changes.
@@ -69,6 +68,7 @@ var actions_data = {};
var setup_wi_toggles = [];
var scroll_trigger_element = undefined; //undefined means not currently set. If set to null, it's disabled.
var drag_id = null;
var story_commentary_characters = {};
const on_colab = $el("#on_colab").textContent == "true";
// Each entry into this array should be an object that looks like:
@@ -141,9 +141,9 @@ let context_menu_cache = [];
const shortcuts = [
{key: "s", desc: "Save Story", func: save_story},
{key: "o", desc: "Open Story", func: load_story_list},
{key: "z", desc: "Undoes last story action", func: () => socket.emit("back", {}), criteria: canNavigateStoryHistory},
{key: "y", desc: "Redoes last story action", func: () => socket.emit("redo", {}), criteria: canNavigateStoryHistory},
{key: "e", desc: "Retries last story action", func: () => socket.emit("retry", {}), criteria: canNavigateStoryHistory},
{key: "z", desc: "Undoes last story action", func: storyBack, criteria: canNavigateStoryHistory},
{key: "y", desc: "Redoes last story action", func: storyRedo, criteria: canNavigateStoryHistory},
{key: "e", desc: "Retries last story action", func: storyRetry, criteria: canNavigateStoryHistory},
{key: "m", desc: "Focuses Memory", func: () => focusEl("#memory")},
{key: "u", desc: "Focuses Author's Note", func: () => focusEl("#authors_notes")}, // CTRL-N is reserved :^(
{key: "g", desc: "Focuses game text", func: () => focusEl("#input_text")},
@@ -221,6 +221,34 @@ function disconnect() {
document.getElementById("disconnect_message").classList.remove("hidden");
}
function storySubmit() {
disruptStoryState();
socket.emit('submit', {'data': document.getElementById('input_text').value, 'theme': document.getElementById('themetext').value});
document.getElementById('input_text').value = '';
document.getElementById('themetext').value = '';
}
function storyBack() {
disruptStoryState();
socket.emit('back', {});
}
function storyRedo() {
disruptStoryState();
socket.emit('redo', {});
}
function storyRetry() {
disruptStoryState();
socket.emit('retry', {});
}
function disruptStoryState() {
// This function is responsible for wiping things away which are sensitive
// to story state
$el("#story-review").classList.add("hidden");
}
function reset_story() {
//console.log("Resetting story");
clearTimeout(calc_token_usage_timeout);
@@ -3117,7 +3145,7 @@ function retry_from_here() {
action_count = parseInt(document.getElementById("action_count").textContent);
//console.log(chunk);
for (let i = 0; i < (action_count-chunk); i++) {
socket.emit('back', {});
storyBack();
}
socket.emit('submit', {'data': "", 'theme': ""});
document.getElementById('input_text').value = '';
@@ -6683,13 +6711,178 @@ function imgGenRetry() {
})();
function requestStoryReview(who, template=null) {
let data = {who: who};
if (template) data.template = template;
socket.emit("story_review", data);
(function() {
const characterContainer = $el(".story-commentary-characters");
const settingsContainer = $el("#story-commentary-settings");
const storyReviewImg = $el("#story-review-img");
function syncCommentatorCards() {
story_commentary_characters = {};
for (const card of document.getElementsByClassName("story-commentary-character")) {
let idString = card.getAttribute("commentator-id");
story_commentary_characters[idString] = card.querySelector(".name").value;
}
socket.emit("var_change", {
ID: "story_commentary_characters",
value: story_commentary_characters
});
}
sync_hooks.push({
class: "story",
name: "commentary_characters",
func: function(commentators) {
$(".story-commentary-character").remove();
for (const [idString, name] of Object.entries(commentators)) {
makeCommentatorCard(idString, name)
}
}
})
function makeCommentatorCard(idString=null, name=null) {
// String due to JS array keys and DOM attributes being strings. Sux!
while (!idString || $el(`[commentator-id="${idString}"`)) {
idString = Math.floor(Math.random() * 1_000_000_000).toString();
}
let card = $e("div", characterContainer, {
classes: ["story-commentary-character"],
"commentator-id": idString,
});
let imageContainer = $e("div", card, {classes: ["image-container"]});
let placeholderImage = $e("span", imageContainer, {
classes: ["material-icons-outlined"],
tooltip: "Upload a picture for this character",
innerText: "add_a_photo"
});
let image = $e("img", imageContainer, {
classes: ["hidden"],
src: `/get_commentator_picture/${idString}`
});
image.addEventListener("load", function() {
image.classList.remove("hidden");
placeholderImage.classList.add("hidden");
});
let input = $e("input", card, {classes: ["name"], placeholder: "Character name"});
if (name) input.value = name;
input.addEventListener("change", syncCommentatorCards);
let deleteButton = $e("span", card, {
classes: ["close", "material-icons-outlined"],
tooltip: "Upload a picture for this character",
innerText: "clear"
});
deleteButton.addEventListener("click", function() {
card.remove();
syncCommentatorCards();
});
const imgInput = $e("input", null, {type: "file", accept: "image/png,image/x-png,image/gif,image/jpeg"});
imgInput.addEventListener("change", async function() {
const file = imgInput.files[0];
if (file.type.split("/")[0] !== "image") {
reportError("Unable to upload commentary image", `File type ${file.type} is not a compatible image type!`)
return;
}
let objectUrl = URL.createObjectURL(file);
placeholderImage.classList.add("hidden");
image.src = objectUrl;
image.classList.remove("hidden");
let r = await fetch(`/set_commentator_picture/${idString}`, {
method: "POST",
body: file
});
});
imageContainer.addEventListener("click", function() {
imgInput.click();
});
characterContainer.scrollIntoView();
}
$el("#story-commentary-settings > .add").addEventListener("click", () => makeCommentatorCard());
async function showStoryReview(data) {
console.log(`${data.who}: ${data.review}`)
storyReviewImg.src = `/get_commentator_picture/${data.id}`;
$el("#story-review-author").innerText = data.who;
$el("#story-review-content").innerText = data.review;
$el("#story-review").classList.remove("hidden");
}
socket.on("show_story_review", showStoryReview);
let x = $el("#story-commentary-enable").querySelector("input")
console.log(x)
// Bootstrap toggle requires jQuery for events
$($el("#story-commentary-enable").querySelector("input")).change(function() {
socket.emit("var_change", {
ID: "story_commentary_enabled",
value: this.checked
});
});
sync_hooks.push({
class: "story",
name: "commentary_enabled",
func: function(commentaryEnabled) {
if (commentaryEnabled) {
settingsContainer.classList.remove("disabled");
} else {
settingsContainer.classList.add("disabled");
}
}
});
storyReviewImg.addEventListener("error", function() {
if (storyReviewImg.src === "/static/default_pfp.png") {
// Something has gone horribly wrong
return;
}
storyReviewImg.src = "/static/default_pfp.png";
});
$el("#story-review-img").addEventListener
})();
for (const el of document.querySelectorAll("[sync-var]")) {
let varName = el.getAttribute("sync-var");
el.addEventListener("change", function() {
sync_to_server(this);
});
const proxy = $el(`[sync-proxy-host="${varName}"]`);
if (proxy) {
el.addEventListener("input", function() {
proxy.value = this.value;
});
}
let slug = varName.replaceAll(".", "_");
el.classList.add("var_sync_" + slug);
}
function showStoryReview(data) {
console.log(`${data.who}: ${data.review}`)
for (const proxy of document.querySelectorAll("[sync-proxy-host]")) {
let varName = proxy.getAttribute("sync-proxy-host");
const hostEl = $el(`[sync-var="${varName}"]`);
if (!hostEl) {
throw Error(`Bad sync proxy host ${varName}`)
}
proxy.addEventListener("change", function() {
hostEl.value = this.value;
socket.emit("var_change", {
ID: varName.replaceAll(".", "_"),
value: this.value
});
});
}

View File

@@ -60,6 +60,13 @@
<div id="action_count" class="var_sync_actions_Action_Count hidden"></div>
<div id="Select Options" class="sequence_area"></div>
<!-- Story Review -->
<div id="story-review" class="hidden">
<img id="story-review-img">
<span id="story-review-author">Bob McBobhead</span>
<span id="story-review-content">Wow, this is a great story. And I mean that. It's positively stelar.</span>
</div>
<!------------ Theme Area--------------------->
<div class="themerow" id="themerow">
<div class="tabrow nomenu_icon">
@@ -102,13 +109,11 @@
<button type="button" class="btn action_button" style="width: 30px; padding: 0px;" onclick='play_pause_tts()' aria-label="play"><span id="play_tts" class="material-icons-outlined" style="font-size: 1.4em;">play_arrow</span></button>
<button type="button" class="btn action_button" style="width: 30px; padding: 0px;" onclick='stop_tts()' aria-label="play"><span id="stop_tts" class="material-icons-outlined" style="font-size: 1.4em;">stop</span></button>
</span>
<button type="button" class="btn action_button submit var_sync_alt_system_aibusy" system_aibusy=False id="btnsubmit"
onclick="socket.emit('submit', {'data': document.getElementById('input_text').value, 'theme': document.getElementById('themetext').value});document.getElementById('input_text').value = '';document.getElementById('themetext').value = '';"
>Submit</button>
<button type="button" class="btn action_button submit var_sync_alt_system_aibusy" system_aibusy=False id="btnsubmit" onclick="storySubmit();">Submit</button>
<button type="button" class="btn action_button submited var_sync_alt_system_aibusy" system_aibusy=False id="btnsent"><img id="thinking" src="static/thinking.gif" class="force_center" onclick="socket.emit('abort','');"></button>
<button type="button" class="btn action_button back" onclick="socket.emit('back', {});" aria-label="undo"><span class="material-icons-outlined" style="font-size: 1.4em;">replay</span></button>
<button type="button" class="btn action_button redo" onclick="socket.emit('redo', {});" aria-label="redo"><span class="material-icons-outlined" style="font-size: 1.4em;">arrow_forward</span></button>
<button type="button" class="btn action_button retry" onclick="socket.emit('retry', {});" aria-label="retry"><span class="material-icons-outlined" style="font-size: 1.4em;">autorenew</span></button>
<button type="button" class="btn action_button back" onclick="storyBack();" aria-label="undo"><span class="material-icons-outlined" style="font-size: 1.4em;">replay</span></button>
<button type="button" class="btn action_button redo" onclick="storyRedo();" aria-label="redo"><span class="material-icons-outlined" style="font-size: 1.4em;">arrow_forward</span></button>
<button type="button" class="btn action_button retry" onclick="storyRetry();" aria-label="retry"><span class="material-icons-outlined" style="font-size: 1.4em;">autorenew</span></button>
</div>
</div>

View File

@@ -239,7 +239,7 @@
<div class="collapsable_header" onclick="toggle_setting_category(this);">
<h4 style="width:var(--flyout_menu_width);"><span class="material-icons-outlined cursor">expand_more</span> Biasing</h4>
</div>
<div id="biasing"class="biasing" ui_level=1>
<div id="biasing" class="biasing" ui_level=1>
<div class="help_text">Influence the likelihood for the AI to output certain phrases.</div>
<div class="bias_header">
<div class="bias_header_phrase">Phrase</div>
@@ -250,6 +250,41 @@
</div>
</div>
</div>
<!-- Story Review Bot -->
<div class="collapsable_header" onclick="toggle_setting_category(this);">
<h4 style="width: var(--flyout_menu_width);"><span class="material-icons-outlined cursor">expand_more</span> Story Commentary</h4>
</div>
<div id="story-commentary" class="story-commentary setting_tile_area" ui_level=1>
<div class="help_text">Allow custom characters to comment on the story.</div>
<div id="story-commentary-enable" class="wide-boolean-setting">
<span>Enable Story Commentary</span>
<input type=checkbox class="setting_item_input" data-size="mini" data-onstyle="success" data-toggle="toggle">
</div>
<div id="story-commentary-settings">
<div class="dynamic-setting-container">
<div class="dynamic-setting-top">
<span class="setting_label"><span>Commentary Chance Percent</span></span>
<input sync-proxy-host="story.commentary_chance" class="setting_value" inputmode="numeric" value="0">
</div>
<span class="setting_item">
<input type="range" sync-var="story.commentary_chance" class="setting_item_input" min="0" max="100" step="1" value="0">
</span>
<div class="dynamic-setting-bounds">
<span class="setting_minlabel">0</span>
<span class="setting_maxlabel">100</span>
</div>
</div>
<p>Commentators</p>
<div class="story-commentary-characters"></div>
<span class="add material-icons-outlined cursor" tooltip="Add Character">add</span>
</div>
</div>
</div>
<div id="setting_menu_interface" class="hidden settings_category_area tab-target tab-target-settings">
<div class="collapsable_header" onclick="toggle_setting_category(this);">
@@ -427,19 +462,19 @@
</div>
<div class="setting_tile_area hidden" id="Tweaks">
<div class="help_text">Small UI changes that can be mixed and matched.</div>
<div class="tweak-container" tweak-path="hide-timing">
<div class="wide-boolean-setting tweak-container" tweak-path="hide-timing">
<span>Hide timing information</span>
<input type=checkbox class="setting_item_input" data-size="mini" data-onstyle="success" data-toggle="toggle">
</div>
<div class="tweak-container" tweak-path="hide-token-bar">
<div class="wide-boolean-setting tweak-container" tweak-path="hide-token-bar">
<span>Hide token bar</span>
<input type=checkbox class="setting_item_input" data-size="mini" data-onstyle="success" data-toggle="toggle">
</div>
<div class="tweak-container" tweak-path="hide-max-length">
<div class="wide-boolean-setting tweak-container" tweak-path="hide-max-length">
<span>Hide text highlighting</span>
<input type=checkbox class="setting_item_input" data-size="mini" data-onstyle="success" data-toggle="toggle">
</div>
<div class="tweak-container" tweak-path="hide-welcome-logo">
<div class="wide-boolean-setting tweak-container" tweak-path="hide-welcome-logo">
<span>Hide welcome logo</span>
<input type=checkbox class="setting_item_input" data-size="mini" data-onstyle="success" data-toggle="toggle">
</div>