diff --git a/public/KoboldAI Worlds/Sample.json b/public/KoboldAI Worlds/Sample.json
index 7449086cf..082684e84 100644
--- a/public/KoboldAI Worlds/Sample.json
+++ b/public/KoboldAI Worlds/Sample.json
@@ -1,6 +1,6 @@
{
"folders": {
- "New Folder": [
+ "Sample Folder": [
0,
1
]
@@ -8,58 +8,23 @@
"entries": {
"0": {
"uid": 0,
- "title": "New World Info Entry",
- "key": [
- "Test",
- "test"
- ],
- "keysecondary": [
- "ttest "
- ],
- "folder": "New Folder",
+ "title": "AAA",
+ "key": [ "AAA" ],
+ "keysecondary": [ ],
"constant": false,
- "manual_text": "tststststs",
- "content": "tststststs",
- "comment": "",
- "type": "wi",
- "token_length": 0,
- "selective": true,
- "used_in_game": false,
- "wpp": {
- "name": "",
- "type": "",
- "format": "W++",
- "attributes": {}
- },
- "use_wpp": false,
- "object_type": null
+ "content": "AAA is a city where BBB lives.",
+ "comment": "AAA definition",
+ "selective": true
},
"1": {
"uid": 1,
- "title": "New World Info Entry",
- "key": [
- "kkkk"
- ],
- "keysecondary": [
- "kkkkk"
- ],
- "folder": "New Folder",
+ "title": "BBB",
+ "key": [ "BBB" ],
+ "keysecondary": [ ],
"constant": false,
- "manual_text": "gsfgsafgasgsf",
- "content": "gsfgsafgasgsf",
- "comment": "",
- "type": "wi",
- "token_length": 0,
- "selective": true,
- "used_in_game": false,
- "wpp": {
- "name": "",
- "type": "",
- "format": "W++",
- "attributes": {}
- },
- "use_wpp": false,
- "object_type": null
+ "content": "BBB is a 21-year old female student of CCC academy.",
+ "comment": "BBB definition",
+ "selective": true
}
}
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index b653f747b..bcac932e7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -105,6 +105,7 @@
var kobold_world = null;
var koboldai_world_names;
var kobold_world_synced = false;
+ var kobold_sync_failed = false;
var max_context = 2048;//2048;
var rep_pen = 1;
var rep_pen_size = 100;
@@ -194,7 +195,6 @@
$("#online_status_indicator3").css("background-color", "green");
$("#online_status_text3").html(online_status);
}
-
}
async function getLastVersion(){
@@ -254,6 +254,9 @@
online_status = data.result;
if(online_status == undefined){
online_status = 'no_connection';
+
+ kobold_world_synced = false;
+ updateWorldStatus();
}
if(online_status.toLowerCase().indexOf('pygmalion') != -1){
is_pygmalion = true;
@@ -274,12 +277,18 @@
console.log(jqXHR);
online_status = 'no_connection';
+ // invalidate world info when losing connection to kobold
+ kobold_world_synced = false;
+ updateWorldStatus();
+
resultCheckStatus();
}
});
}else{
if(is_get_status_novel != true){
online_status = 'no_connection';
+ kobold_world_synced = false;
+ updateWorldStatus();
}
}
}
@@ -900,7 +909,8 @@
var generate_data;
if(main_api == 'kobold'){
- var generate_data = {prompt: finalPromt, gui_settings: true,max_length: amount_gen,temperature: temp, max_context_length: max_context};
+ const use_world_info = Boolean(kobold_world && kobold_world_synced);
+ var generate_data = {prompt: finalPromt, gui_settings: true,max_length: amount_gen,temperature: temp, max_context_length: max_context, use_world_info};
if(preset_settings != 'gui'){
var this_settings = koboldai_settings[koboldai_setting_names[preset_settings]];
@@ -941,7 +951,8 @@
s4:this_settings.sampler_order[3],
s5:this_settings.sampler_order[4],
s6:this_settings.sampler_order[5],
- s7:this_settings.sampler_order[6]
+ s7:this_settings.sampler_order[6],
+ use_world_info: use_world_info,
};
}
}
@@ -1203,6 +1214,9 @@
$( "#rm_button_characters" ).children("h2").css(deselected_button_style);
$( "#rm_button_settings" ).children("h2").css(seleced_button_style);
$( "#rm_button_selected_ch" ).children("h2").css(deselected_button_style);
+
+ // Dumb call, but won't need an interval
+ updateWorldStatus();
});
$( "#rm_button_characters" ).click(function() {
selected_button = 'characters';
@@ -1934,6 +1948,7 @@
$("#world_info").change(function() {
const selectedWorld = $('#world_info').find(":selected").val();
kobold_world_synced = false;
+ kobold_sync_failed = false;
kobold_world = null;
if (selectedWorld !== 'None') {
@@ -1942,6 +1957,8 @@
}
syncKoboldWorldInfo(true);
+ saveSettings();
+ updateWorldStatus();
});
$( "#settings_perset" ).change(function() {
@@ -2032,6 +2049,8 @@
$('#amount_gen_block').css('display', 'none');
$('#world_info').css('display', 'none');
}
+
+ updateWorldStatus();
}
async function getUserAvatars(){
const response = await fetch("/getuseravatars", {
@@ -2056,14 +2075,36 @@
}
}
- async function syncKoboldWorldInfo(force) {
- // Don't sync if no world selected
- if (!kobold_world || online_status == 'no_connection') {
- return;
- }
- // Don't sync if synced and not forcing
- if (kobold_world_synced && !force) {
+ function updateWorldStatus() {
+ if($('#world_info').is(':visible') && kobold_world) {
+ $('#world_status').show();
+
+ if (kobold_world_synced) {
+ $("#world_status_indicator").css("background-color", "green");
+ $("#world_status_text").html("Synchronized with KoboldAI")
+ }
+ else {
+ let statusText = online_status === 'no_connection'
+ ? "Waiting for connection"
+ : "Synchronizing...";
+
+ if (kobold_sync_failed) {
+ statusText = "Synchronization failed (see console)";
+ }
+
+ $("#world_status_text").html(statusText);
+ $("#world_status_indicator").css("background-color", "red");
+ }
+ } else {
+ $('#world_status').hide();
+ }
+ }
+
+ async function syncKoboldWorldInfo(force) {
+ // Don't sync if no world selected or if synced and not forcing
+ if (!kobold_world || online_status === 'no_connection' || (kobold_world_synced && !force)) {
+ updateWorldStatus();
return;
}
@@ -2078,12 +2119,28 @@
if (syncData.ok) {
kobold_world_synced = true;
+ kobold_sync_failed = false;
}
if (syncData.busy) {
- console.log('Sync API is busy');
+ console.log('Sync API is busy. Retrying in 0.5sec');
+ setTimeout(() => syncKoboldWorldInfo(force), 500);
+ return;
}
+ } else {
+ kobold_sync_failed = true;
+ let responseLog = response.statusText;
+
+ try {
+ var responseBody = await response.text();
+ responseLog += ('\n' + responseBody);
+ } catch {
+ // empty catch
+ }
+ console.error(`Sync API response: ${responseLog}`);
}
+
+ updateWorldStatus();
}
$(document).on('input', '#temp', function() {
@@ -2228,10 +2285,25 @@
koboldai_setting_names = {};
koboldai_setting_names = arr_holder;
- koboldai_world_names = data.koboldai_world_names;
+ // world info settings
+ koboldai_world_names = data.koboldai_world_names?.length ? data.koboldai_world_names : [];
+
+ if(settings.kobold_world != undefined) {
+ if (koboldai_world_names.includes(settings.kobold_world)) {
+ kobold_world = settings.kobold_world;
+ kobold_world_synced = false;
+ kobold_sync_failed = false;
+ }
+ }
+
koboldai_world_names.forEach((item, i) => {
$('#world_info').append(``);
+ // preselect world if saved
+ if (item == kobold_world){
+ $(`#world_info option[value=${i}]`).attr('selected', 'true');
+ }
});
+ // end world info settings
preset_settings = settings.preset_settings;
@@ -2308,7 +2380,6 @@
preset_settings = 'gui';
$("#settings_perset option[value=gui]").attr('selected', 'true');
}
-
}
//User
@@ -2358,7 +2429,8 @@
model_novel: model_novel,
temp_novel: temp_novel,
rep_pen_novel: rep_pen_novel,
- rep_pen_size_novel: rep_pen_size_novel
+ rep_pen_size_novel: rep_pen_size_novel,
+ kobold_world: kobold_world,
}),
beforeSend: function(){
@@ -3003,6 +3075,9 @@
+

diff --git a/public/style.css b/public/style.css
index f5f5a6693..fa81fdeca 100644
--- a/public/style.css
+++ b/public/style.css
@@ -953,6 +953,27 @@ input[type=button] {
margin-left: 4px;
display: inline-block;
}
+
+#world_info {
+ margin-bottom: 12px;
+}
+#world_status{
+ opacity: 0.5;
+ margin-top: 2px;
+ margin-left: 10px;
+}
+#world_status_indicator{
+ border-radius: 7px;
+ width: 14px;
+ height: 14px;
+ background-color: red;
+ display: inline-block;
+}
+#world_status_text{
+ margin-left: 4px;
+ display: inline-block;
+}
+
.del_checkbox{
display: none;
opacity: 0.5;
diff --git a/server.js b/server.js
index 25e134107..e39330526 100644
--- a/server.js
+++ b/server.js
@@ -48,7 +48,7 @@ var is_colab = false;
const jsonParser = express.json({limit: '100mb'});
const urlencodedParser = express.urlencoded({extended: true, limit: '100mb'});
-
+const baseRequestArgs = { headers: { "Content-Type": "application/json" } };
app.use(function (req, res, next) { //Security
const clientIp = req.connection.remoteAddress.split(':').pop();
@@ -699,10 +699,10 @@ app.post('/synckoboldworld', jsonParser, async (request, response) => {
const worldName = request.body.name;
await synchronizeKoboldWorldInfo(worldName);
response.send({ ok: true });
- console.log('World info synchronized with Kobold');
} catch (err) {
- console.error(`Error during world synchronization: ${JSON.stringify(err)}`);
- response.sendStatus(500);
+ var message = JSON.stringify(err);
+ console.error(`Error during world synchronization: ${message}`);
+ response.status(500).send(message);
} finally {
kobold_world_sync_busy = false;
}
@@ -1072,10 +1072,11 @@ app.post("/importchat", urlencodedParser, function(request, response){
function findTavernWorldEntry(info, key) {
for (const entryId in info.entries) {
- const keyString = info.entries[entryId].key.join(',');
+ const entry = info.entries[entryId];
+ const keyString = entry.key.join(',');
if (keyString === key) {
- return info.entries[entryId];
+ return entry;
}
}
@@ -1094,8 +1095,6 @@ async function synchronizeKoboldWorldInfo(worldInfoName) {
const tavernWorldInfoText = fs.readFileSync(pathToWorldInfo, 'utf8');
const tavernWorldInfo = JSON.parse(tavernWorldInfoText);
- const baseRequestArgs = { headers: { "Content-Type": "application/json" } };
-
// Get existing world info
const koboldWorldInfo = await getAsync(`${api_server}/v1/world_info`, baseRequestArgs);
@@ -1110,38 +1109,69 @@ async function synchronizeKoboldWorldInfo(worldInfoName) {
// Create folder if not already exists
if (shouldCreateWorld) {
- const createdFolder = await postAsync(`${api_server}/v1/world_info/folders`, { data: {}, ...baseRequestArgs });
- koboldWorldUid = createdFolder.uid;
-
- // Set a name so we could find the folder later
- const setNameArgs = { data: { value: koboldFolderName }, ...baseRequestArgs };
- await putAsync(`${api_server}/v1/world_info/folders/${koboldWorldUid}/name`, setNameArgs);
-
- // Create all world info entries
- tavernEntriesToCreate.push(...Object.keys(tavernWorldInfo.entries));
+ koboldWorldUid = await createKoboldFolder(koboldFolderName, tavernEntriesToCreate, tavernWorldInfo);
}
- if (koboldFoldersToDelete.length) {
- await Promise.all(koboldFoldersToDelete.map((uid) => deleteAsync(api_server + `/v1/world_info/folders/${uid}`, baseRequestArgs)));
- }
+ await deleteKoboldFolders(koboldFoldersToDelete);
+ await deleteKoboldEntries(koboldEntriesToDelete);
+ await createTavernEntries(tavernEntriesToCreate, koboldWorldUid, tavernWorldInfo);
+}
- if (koboldEntriesToDelete.length) {
- await Promise.all(koboldEntriesToDelete.map((uid) => deleteAsync(`${api_server}/v1/world_info/${uid}`)));
- }
+async function createKoboldFolder(koboldFolderName, tavernEntriesToCreate, tavernWorldInfo) {
+ const createdFolder = await postAsync(`${api_server}/v1/world_info/folders`, { data: {}, ...baseRequestArgs });
+ const koboldWorldUid = createdFolder.uid;
+ // Set a name so we could find the folder later
+ const setNameArgs = { data: { value: koboldFolderName }, ...baseRequestArgs };
+ await putAsync(`${api_server}/v1/world_info/folders/${koboldWorldUid}/name`, setNameArgs);
+
+ // Create all world info entries
+ tavernEntriesToCreate.push(...Object.keys(tavernWorldInfo.entries));
+ return koboldWorldUid;
+}
+
+async function createTavernEntries(tavernEntriesToCreate, koboldWorldUid, tavernWorldInfo) {
if (tavernEntriesToCreate.length && koboldWorldUid) {
- for (const tavernUid in tavernEntriesToCreate) {
- const tavernEntry = tavernWorldInfo.entries[tavernUid];
- const koboldEntry = await postAsync(`${api_server}/v1/world_info/folders/${koboldWorldUid}`, { data: {}, ...baseRequestArgs });
- await setKoboldEntryData(tavernEntry, koboldEntry);
+ for (const tavernUid of tavernEntriesToCreate) {
+ try {
+ const tavernEntry = tavernWorldInfo.entries[tavernUid];
+ const koboldEntry = await postAsync(`${api_server}/v1/world_info/folders/${koboldWorldUid}`, { data: {}, ...baseRequestArgs });
+ await setKoboldEntryData(tavernEntry, koboldEntry);
+ } catch (err) {
+ console.error(`Couldn't create Kobold world info entry, tavernUid=${tavernUid}. Skipping...`);
+ console.error(err);
+ }
+ }
+ }
+}
+
+async function deleteKoboldEntries(koboldEntriesToDelete) {
+ if (koboldEntriesToDelete.length) {
+ for (const uid of koboldEntriesToDelete) {
+ try {
+ await deleteAsync(`${api_server}/v1/world_info/${uid}`);
+ } catch (err) {
+ console.error(`Couldn't delete Kobold world info entry, uid=${uid}. Skipping...`);
+ console.error(err);
+ }
+ }
+ }
+}
+
+async function deleteKoboldFolders(koboldFoldersToDelete) {
+ if (koboldFoldersToDelete.length) {
+ for (const uid of koboldFoldersToDelete) {
+ try {
+ await deleteAsync(api_server + `/v1/world_info/folders/${uid}`, baseRequestArgs);
+ } catch (err) {
+ console.error(`Couldn't delete Kobold world info folder, uid=${uid}. Skipping...`);
+ console.error(err);
+ }
}
}
}
async function setKoboldEntryData(tavernEntry, koboldEntry) {
- const baseRequestArgs = { headers: { "Content-Type": "application/json" } };
- const setDataRequests = [];
-
// 1. Set primary key
if (tavernEntry.key?.length) {
const keyArgs = { data: { value: tavernEntry.key.join(',') }, ...baseRequestArgs };
@@ -1179,8 +1209,6 @@ async function setKoboldEntryData(tavernEntry, koboldEntry) {
await putToPromise(`${api_server}/v1/world_info/${koboldEntry.uid}/selective`, selectiveArgs);
}
*/
-
- return setDataRequests;
}
async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavernWorldInfo) {
@@ -1194,8 +1222,7 @@ async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavern
if (koboldWorldInfo?.folders?.length) {
let existingFolderAlreadyFound = false;
- for (const folderUid in koboldWorldInfo.folders) {
- const folder = koboldWorldInfo.folders[folderUid];
+ for (const folder of koboldWorldInfo.folders) {
// Don't care about non-Tavern folders
if (!isTavernKoboldWorldInfo(folder.name)) {
continue;
@@ -1213,8 +1240,7 @@ async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavern
koboldWorldUid = folder.uid;
if (folder.entries?.length) {
const foundTavernEntries = [];
- for (const koboldEntryUid in folder.entries) {
- const koboldEntry = folder.entries[koboldEntryUid];
+ for (const koboldEntry of folder.entries) {
const tavernEntry = findTavernWorldEntry(tavernWorldInfo, koboldEntry.key);
if (tavernEntry) {
@@ -1232,9 +1258,11 @@ async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavern
}
// Check if every tavern entry was found in kobold world
+ // BTW. Entries is an object, not an array!
for (const tavernEntryUid in tavernWorldInfo.entries) {
- if (!foundTavernEntries.includes(tavernWorldInfo.entries[tavernEntryUid].uid)) {
- tavernEntriesToCreate.push(tavernWorldInfo.entries[tavernEntryUid].uid);
+ const tavernEntry = tavernWorldInfo.entries[tavernEntryUid];
+ if (!foundTavernEntries.includes(tavernEntry.uid)) {
+ tavernEntriesToCreate.push(tavernEntry.uid);
}
}
}