Merge pull request #132 from one-some/ui2-club-import-overhaul

Club import overhaul
This commit is contained in:
ebolam
2022-09-16 08:17:15 -04:00
committed by GitHub
5 changed files with 319 additions and 4 deletions

View File

@@ -6,6 +6,7 @@
#==================================================================#
# External packages
from dataclasses import dataclass
import eventlet
eventlet.monkey_patch(all=True, thread=False, os=False)
import os
@@ -239,6 +240,193 @@ class Send_to_socketio(object):
def flush(self):
pass
@dataclass
class ImportBuffer:
# Singleton!!!
prompt: Optional[str] = None
memory: Optional[str] = None
authors_note: Optional[str] = None
notes: Optional[str] = None
world_infos: Optional[dict] = None
@dataclass
class PromptPlaceholder:
id: str
order: Optional[int] = None
default: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
value: Optional[str] = None
def to_json(self) -> dict:
return {key: getattr(self, key) for key in [
"id",
"order",
"default",
"title",
"description"
]}
def request_client_configuration(self, placeholders: list[PromptPlaceholder]) -> None:
emit("request_prompt_config", [x.to_json() for x in placeholders], broadcast=False, room="UI_2")
def extract_placeholders(self, text: str) -> list[PromptPlaceholder]:
placeholders = []
for match in re.finditer(r"\${(.*?)}", text):
ph_text = match.group(1)
try:
ph_order, ph_text = ph_text.split("#")
except ValueError:
ph_order = None
if "[" not in ph_text:
ph_id = ph_text
# Already have it!
if any([x.id == ph_id for x in placeholders]):
continue
# Apparently, none of these characters are supported:
# "${}[]#:@^|", however I have found some prompts using these,
# so they will be allowed.
for char in "${}[]":
if char in ph_text:
print("[eph] Weird char")
print(f"{char=}")
print(f"{ph_id=}")
return
placeholders.append(self.PromptPlaceholder(
id=ph_id,
order=int(ph_order) if ph_order else None,
))
continue
ph_id, _ = ph_text.split("[")
ph_text = ph_text.replace(ph_id, "", 1)
# Already have it!
if any([x.id == ph_id for x in placeholders]):
continue
# Match won't match it for some reason (???), so we use finditer and next()
try:
default_match = next(re.finditer(r"\[(.*?)\]", ph_text))
except StopIteration:
print("[eph] Weird brackets")
return placeholders
ph_default = default_match.group(1)
ph_text = ph_text.replace(default_match.group(0), "")
try:
ph_title, ph_desc = ph_text.split(":")
except ValueError:
ph_title = ph_text or None
ph_desc=None
placeholders.append(self.PromptPlaceholder(
id=ph_id,
order=int(ph_order) if ph_order else None,
default=ph_default,
title=ph_title,
description=ph_desc
))
return placeholders
def _replace_placeholders(self, text: str, ph_ids: dict):
for ph_id, value in ph_ids.items():
pattern = "\${(?:\d#)?%s.*?}" % re.escape(ph_id)
for ph_text in re.findall(pattern, text):
text = text.replace(ph_text, value)
return text
def replace_placeholders(self, ph_ids: dict):
self.prompt = self._replace_placeholders(self.prompt, ph_ids)
self.memory = self._replace_placeholders(self.memory, ph_ids)
self.authors_note = self._replace_placeholders(self.authors_note, ph_ids)
for i in range(len(self.world_infos)):
for key in ["content", "comment"]:
self.world_infos[i][key] = self._replace_placeholders(self.world_infos[i][key])
def from_club(self, club_id):
# Maybe it is a better to parse the NAI Scenario (if available), it has more data
r = requests.get(f"https://aetherroom.club/api/{club_id}")
if not r.ok:
# TODO: Show error message on client
print(f"[import] Got {r.status_code} on request to club :^(")
return
j = r.json()
self.prompt = j["promptContent"]
self.memory = j["memory"]
self.authors_note = j["authorsNote"]
self.notes = j["description"]
self.world_infos = []
for wi in j["worldInfos"]:
self.world_infos.append({
"key_list": wi["keysList"],
"keysecondary": [],
"content": wi["entry"],
"comment": "",
"folder": wi.get("folder", None),
"num": 0,
"init": True,
"selective": wi.get("selective", False),
"constant": wi.get("constant", False),
"uid": None,
})
placeholders = self.extract_placeholders(self.prompt)
if not placeholders:
self.commit()
else:
self.request_client_configuration(placeholders)
def commit(self):
# Push buffer story to actual story
exitModes()
koboldai_vars.create_story("")
koboldai_vars.gamestarted = True
koboldai_vars.prompt = self.prompt
koboldai_vars.memory = self.memory or ""
koboldai_vars.authornote = self.authors_note or ""
koboldai_vars.notes = self.notes
# ???: Was this supposed to increment?
num = 0
for wi in self.world_infos:
# koboldai_vars.worldinfo += self.world_infos
koboldai_vars.worldinfo_v2.add_item(
wi["key_list"][0],
wi["key_list"],
wi.get("keysecondary", []),
wi.get("folder", "root"),
wi.get("constant", False),
wi["content"],
wi.get("comment", "")
)
# Reset current save
koboldai_vars.savedir = getcwd()+"\\stories"
# Refresh game screen
koboldai_vars.laststory = None
setgamesaved(False)
sendwi()
refresh_story()
import_buffer = ImportBuffer()
# Set logging level to reduce chatter from Flask
import logging
@@ -7132,6 +7320,11 @@ def get_files_sorted(path, sort, desc=False):
return [key[0] for key in sorted(data.items(), key=lambda kv: (kv[1], kv[0]), reverse=desc)]
@socketio.on("configure_prompt")
def UI_2_configure_prompt(data):
import_buffer.replace_placeholders(data)
import_buffer.commit()
#==================================================================#
# Event triggered when browser SocketIO detects a variable change
#==================================================================#
@@ -7790,7 +7983,8 @@ def UI_2_unload_userscripts(data):
def UI_2_load_aidg_club(data):
if koboldai_vars.debug:
print("Load aidg.club: {}".format(data))
importAidgRequest(data)
import_buffer.from_club(data)
# importAidgRequest(data)
#==================================================================#

View File

@@ -90,7 +90,7 @@ class koboldai_vars(object):
else:
self._story_settings[story_name] = story_settings(self.socketio)
if json_data is not None:
self.load_story(sotry_name, json_data)
self.load_story(story_name, json_data)
self._story_settings['default'].send_to_ui()
def story_list(self):

View File

@@ -1854,7 +1854,8 @@ body {
/* Finder */
#finder-container,
#debug-file-container {
#debug-file-container,
#prompt-config-container {
display: flex;
justify-content: center;
align-items: center;
@@ -2060,6 +2061,66 @@ body {
background-color: rgb(158, 2, 2);
}
/* Prompt Config */
#prompt-config {
display: flex;
flex-direction: column;
position: relative;
background-color: var(--popup_background_color);
width: 25%;
height: 75%;
}
#prompt-config-header {
/* HACK: Need to add (or use) a theme color for this! */
background-color: #212a33;
padding: 7px;
}
#prompt-config-header > h3 {
margin-top: 5px;
margin-bottom: 5px;
}
#prompt-config-placeholders {
display: flex;
flex-direction: column;
row-gap: 16px;
overflow-y: scroll;
padding: 7px;
padding-left: 14px
}
.prompt-config-title {
display: block;
font-size: 20px;
font-weight: bold;
}
.prompt-config-value {
padding: 7px;
}
#prompt-config-done {
display: flex;
justify-content: center;
align-items: center;
width: 100px;
height: 32px;
position: absolute;
right: 10px;
bottom: 10px;
cursor: pointer;
color: var(--button_text);
background-color: var(--button_background);
border-radius: var(--radius_settings_button);
}
/*---------------------------------- Global ------------------------------------------------*/
.hidden {
display: none;

View File

@@ -28,6 +28,7 @@ socket.on("error", function(data){show_error_message(data);});
socket.on('load_cookies', function(data){load_cookies(data)});
socket.on('load_tweaks', function(data){load_tweaks(data);});
socket.on("wi_results", updateWISearchListings);
socket.on("request_prompt_config", configurePrompt);
//socket.onAny(function(event_name, data) {console.log({"event": event_name, "class": data.classname, "data": data});});
var presets = {};
@@ -3432,6 +3433,51 @@ async function downloadDebugFile(redact=true) {
downloadString(JSON.stringify(debug_info, null, 4), "kobold_debug.json");
}
function configurePrompt(placeholderData) {
console.log(placeholderData);
const container = document.querySelector("#prompt-config-container");
container.classList.remove("hidden");
const placeholders = document.querySelector("#prompt-config-placeholders");
for (const phData of placeholderData) {
let placeholder = $e("div", placeholders, {classes: ["prompt-config-ph"]});
// ${character.name} is an AI Dungeon thing, although I believe NAI
// supports it as well. Many prompts use it. I think this is the only
// hardcoded thing like this.
let titleText = phData.title || phData.id;
if (titleText === "character.name") titleText = "Character Name";
let title = $e("span", placeholder, {classes: ["prompt-config-title"], innerText: titleText});
if (phData.description) $e("span", placeholder, {
classes: ["prompt-config-desc", "help_text"],
innerText: phData.description
});
let input = $e("input", placeholder, {
classes: ["prompt-config-value"],
value: phData.default || "",
placeholder: phData.default || "",
"placeholder-id": phData.id
});
}
}
function sendPromptConfiguration() {
let data = {};
for (const configInput of document.querySelectorAll(".prompt-config-value")) {
data[configInput.getAttribute("placeholder-id")] = configInput.value;
}
socket.emit("configure_prompt", data);
document.querySelector("#prompt-config-container").classList.add("hidden");
$(".prompt-config-ph").remove();
}
function loadNAILorebook(data, filename) {
let lorebookVersion = data.lorebookVersion;
let wi_data = {folders: {[filename]: []}, entries: {}};

View File

@@ -113,7 +113,7 @@
<input autocomplete="off" class="form-control" type="text" placeholder="Prompt Number (4-digit number at the end of aetherroom.club URL)" id="aidgpromptnum">
</div>
<div class="popup_load_cancel">
<button type="button" class="btn btn-primary popup_load_cancel_button" onclick="socket.emit('load_aidg_club', document.getElementById('aidgpromptnum').value); this.parentElement.parentElement.classList.add('hidden');">Accept</button>
<button type="button" class="btn btn-primary popup_load_cancel_button" onclick="socket.emit('load_aidg_club', document.getElementById('aidgpromptnum').value); $('.popup').addClass('hidden');">Accept</button>
<button type="button" class="btn btn-primary popup_load_cancel_button" onclick="this.parentElement.parentElement.classList.add('hidden');">Cancel</button>
</div>
</div>
@@ -192,4 +192,18 @@
</span>
</div>
</div>
</div>
<!---------------- Prompt Config ------------------->
<div id="prompt-config-container" class="hidden">
<div id="prompt-config">
<div id="prompt-config-header">
<h3 class="noselect">Prompt Configuration</h3>
<span class="help_text">This prompt has placeholders you need to fill in before starting.</span>
</div>
<!-- Order, default, title, description -->
<div id="prompt-config-placeholders"></div>
<div id="prompt-config-done" onclick="sendPromptConfiguration();">Done</div>
</div>
</div>