From 555ca5fd05e4f46f61e8a884179924767b99c398 Mon Sep 17 00:00:00 2001 From: somebody Date: Thu, 11 Aug 2022 17:31:12 -0500 Subject: [PATCH 1/4] Add token usage indicator --- aiserver.py | 31 +++++++++++++++++++++++++++ static/application.js | 49 +++++++++++++++++++++++++++++++++++++++++++ static/custom.css | 27 ++++++++++++++++++++++++ templates/index.html | 4 ++-- 4 files changed, 109 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index cd8b081e..8f511b01 100644 --- a/aiserver.py +++ b/aiserver.py @@ -3608,6 +3608,37 @@ def get_message(msg): emit('from_server', {'cmd': 'set_debug', 'data': msg['data']}, broadcast=True) if vars.debug: send_debug() + elif(msg['cmd'] == 'getfieldbudget'): + unencoded = msg["data"]["unencoded"] + field = msg["data"]["field"] + + # Tokenizer may be undefined here when a model has not been chosen. + if "tokenizer" not in globals(): + # We don't have a tokenizer, just return nulls. + emit( + 'from_server', + {'cmd': 'showfieldbudget', 'data': {"length": None, "max": None, "field": field}}, + broadcast=True + ) + return + + header_length = len(tokenizer._koboldai_header) + max_tokens = vars.max_length - header_length - vars.sp_length - vars.genamt + + if not unencoded: + # Unencoded is empty, just return 0 + emit( + 'from_server', + {'cmd': 'showfieldbudget', 'data': {"length": 0, "max": max_tokens, "field": field}}, + broadcast=True + ) + else: + tokens_length = len(tokenizer.encode(unencoded)) + emit( + 'from_server', + {'cmd': 'showfieldbudget', 'data': {"length": tokens_length, "max": max_tokens, "field": field}}, + broadcast=True + ) #==================================================================# # Send userscripts list to client diff --git a/static/application.js b/static/application.js index 12b8f214..ab4e307a 100644 --- a/static/application.js +++ b/static/application.js @@ -512,6 +512,16 @@ function addWiLine(ob) { $(".wisortable-excluded-dynamic").removeClass("wisortable-excluded-dynamic"); $(this).parent().css("max-height", "").find(".wicomment").find(".form-control").css("max-height", ""); }); + + for (const wientry of document.getElementsByClassName("wientry")) { + // If we are uninitialized, skip. + if ($(wientry).closest(".wilistitem-uninitialized").length) continue; + + // add() will not add if the class is already present + wientry.classList.add("tokens-counted"); + } + + registerTokenCounters(); } function addWiFolder(uid, ob) { @@ -835,6 +845,7 @@ function exitMemoryMode() { button_actmem.html("Memory"); show([button_actback, button_actfwd, button_actretry, button_actwi]); input_text.val(""); + updateInputBudget(input_text[0]); // Hide Author's Note field anote_menu.slideUp("fast"); } @@ -2139,6 +2150,31 @@ function interpolateRGB(color0, color1, t) { ] } +function updateInputBudget(inputElement) { + socket.send({"cmd": "getfieldbudget", "data": {"unencoded": inputElement.value, "field": inputElement.id}}); +} + +function registerTokenCounters() { + // Add token counters to all input containers with the class of "tokens-counted", + // if a token counter is not already a child of said container. + for (const el of document.getElementsByClassName("tokens-counted")) { + if (el.getElementsByClassName("input-token-usage").length) continue; + + let span = document.createElement("span"); + span.classList.add("input-token-usage"); + span.innerText = "?/? Tokens"; + el.appendChild(span); + + let inputElement = el.querySelector("input, textarea"); + + inputElement.addEventListener("input", function() { + updateInputBudget(this); + }); + + updateInputBudget(inputElement); + } +} + //=================================================================// // READY/RUNTIME //=================================================================// @@ -2481,6 +2517,7 @@ $(document).ready(function(){ memorytext = msg.data; input_text.val(msg.data); } + updateInputBudget(input_text[0]); } else if(msg.cmd == "setmemory") { memorytext = msg.data; if(memorymode) { @@ -2602,6 +2639,7 @@ $(document).ready(function(){ } else if(msg.cmd == "setanote") { // Set contents of Author's Note field anote_input.val(msg.data); + updateInputBudget(anote_input[0]); } else if(msg.cmd == "setanotetemplate") { // Set contents of Author's Note Template field $("#anotetemplate").val(msg.data); @@ -2913,6 +2951,12 @@ $(document).ready(function(){ opt.innerHTML = engine[1]; $("#oaimodel")[0].appendChild(opt); } + } else if(msg.cmd == 'showfieldbudget') { + let inputElement = document.getElementById(msg.data.field); + let tokenBudgetElement = inputElement.parentNode.getElementsByClassName("input-token-usage")[0]; + let tokenLength = msg.data.length ?? "?"; + let tokenMax = msg.data.max ?? "?"; + tokenBudgetElement.innerText = `${tokenLength}/${tokenMax} Tokens`; } }); @@ -3381,6 +3425,11 @@ $(document).ready(function(){ if (handled) ev.preventDefault(); }); + + registerTokenCounters(); + + updateInputBudget(input_text[0]); + }); diff --git a/static/custom.css b/static/custom.css index 082c4230..af238dc7 100644 --- a/static/custom.css +++ b/static/custom.css @@ -1695,3 +1695,30 @@ body.connected .popupfooter, .popupfooter.always-available { overflow-x: auto; white-space: nowrap; } + +.tokens-counted { + position: relative; +} + +.input-token-usage { + color: white; + position: absolute; + font-size: 10px; + bottom: 2px; + right: 5px; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Override needed here due to the 10px right padding on inputrowleft; add 10 px. */ +#inputrowleft > .input-token-usage { + right: 15px; + bottom: 1px; +} + +.wientry > .input-token-usage { + bottom: 8px; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 82e4ca93..27b50b78 100644 --- a/templates/index.html +++ b/templates/index.html @@ -157,7 +157,7 @@
-
+
@@ -170,7 +170,7 @@
Author's Note
-
+
From a28faa0cb27bcd6661dd844eb5f9e931c0cbe377 Mon Sep 17 00:00:00 2001 From: somebody Date: Thu, 11 Aug 2022 18:21:49 -0500 Subject: [PATCH 2/4] Fix author's note token usage --- aiserver.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/aiserver.py b/aiserver.py index 8f511b01..955cdd11 100644 --- a/aiserver.py +++ b/aiserver.py @@ -3633,7 +3633,10 @@ def get_message(msg): broadcast=True ) else: + if field == "anoteinput": + unencoded = buildauthorsnote(unencoded) tokens_length = len(tokenizer.encode(unencoded)) + emit( 'from_server', {'cmd': 'showfieldbudget', 'data': {"length": tokens_length, "max": max_tokens, "field": field}}, @@ -3969,6 +3972,12 @@ def actionredo(): #==================================================================# # #==================================================================# +def buildauthorsnote(authorsnote): + # Build Author's Note if set + if authorsnote == "": + return "" + return ("\n" + vars.authornotetemplate + "\n").replace("<|>", authorsnote) + def calcsubmitbudgetheader(txt, **kwargs): # Scan for WorldInfo matches winfo, found_entries = checkworldinfo(txt, **kwargs) @@ -3979,11 +3988,7 @@ def calcsubmitbudgetheader(txt, **kwargs): else: mem = vars.memory - # Build Author's Note if set - if(vars.authornote != ""): - anotetxt = ("\n" + vars.authornotetemplate + "\n").replace("<|>", vars.authornote) - else: - anotetxt = "" + anotetxt = buildauthorsnote(vars.authornote) return winfo, mem, anotetxt, found_entries From c21c1e3dc027c4a0e110db130459ec23febb5c3e Mon Sep 17 00:00:00 2001 From: somebody Date: Thu, 11 Aug 2022 18:22:06 -0500 Subject: [PATCH 3/4] Don't show token usage when max tokens is unknown --- static/application.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/static/application.js b/static/application.js index ab4e307a..b6ba2c49 100644 --- a/static/application.js +++ b/static/application.js @@ -2954,9 +2954,13 @@ $(document).ready(function(){ } else if(msg.cmd == 'showfieldbudget') { let inputElement = document.getElementById(msg.data.field); let tokenBudgetElement = inputElement.parentNode.getElementsByClassName("input-token-usage")[0]; - let tokenLength = msg.data.length ?? "?"; - let tokenMax = msg.data.max ?? "?"; - tokenBudgetElement.innerText = `${tokenLength}/${tokenMax} Tokens`; + if (msg.data.max === null) { + tokenBudgetElement.innerText = ""; + } else { + let tokenLength = msg.data.length ?? "?"; + let tokenMax = msg.data.max ?? "?"; + tokenBudgetElement.innerText = `${tokenLength}/${tokenMax} Tokens`; + } } }); From 6ac970b1c0d14c6cf0384fa06f4c78bd7af86e2f Mon Sep 17 00:00:00 2001 From: somebody Date: Thu, 11 Aug 2022 18:38:29 -0500 Subject: [PATCH 4/4] Update author's template effect token usage live --- aiserver.py | 8 ++++---- static/application.js | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/aiserver.py b/aiserver.py index 955cdd11..641ce6c4 100644 --- a/aiserver.py +++ b/aiserver.py @@ -3634,7 +3634,7 @@ def get_message(msg): ) else: if field == "anoteinput": - unencoded = buildauthorsnote(unencoded) + unencoded = buildauthorsnote(unencoded, msg["data"]["anotetemplate"]) tokens_length = len(tokenizer.encode(unencoded)) emit( @@ -3972,11 +3972,11 @@ def actionredo(): #==================================================================# # #==================================================================# -def buildauthorsnote(authorsnote): +def buildauthorsnote(authorsnote, template): # Build Author's Note if set if authorsnote == "": return "" - return ("\n" + vars.authornotetemplate + "\n").replace("<|>", authorsnote) + return ("\n" + template + "\n").replace("<|>", authorsnote) def calcsubmitbudgetheader(txt, **kwargs): # Scan for WorldInfo matches @@ -3988,7 +3988,7 @@ def calcsubmitbudgetheader(txt, **kwargs): else: mem = vars.memory - anotetxt = buildauthorsnote(vars.authornote) + anotetxt = buildauthorsnote(vars.authornote, vars.authornotetemplate) return winfo, mem, anotetxt, found_entries diff --git a/static/application.js b/static/application.js index b6ba2c49..be8f9bbf 100644 --- a/static/application.js +++ b/static/application.js @@ -2151,7 +2151,13 @@ function interpolateRGB(color0, color1, t) { } function updateInputBudget(inputElement) { - socket.send({"cmd": "getfieldbudget", "data": {"unencoded": inputElement.value, "field": inputElement.id}}); + let data = {"unencoded": inputElement.value, "field": inputElement.id}; + + if (inputElement.id === "anoteinput") { + data["anotetemplate"] = $("#anotetemplate").val(); + } + + socket.send({"cmd": "getfieldbudget", "data": data}); } function registerTokenCounters() { @@ -3430,6 +3436,10 @@ $(document).ready(function(){ if (handled) ev.preventDefault(); }); + $("#anotetemplate").on("input", function() { + updateInputBudget(anote_input[0]); + }) + registerTokenCounters(); updateInputBudget(input_text[0]);