Merge branch 'dev' of https://github.com/BlipRanger/SillyTavern into feature/stats

This commit is contained in:
BlipRanger
2023-07-18 09:42:11 -04:00
53 changed files with 1564 additions and 455 deletions

1
.gitignore vendored
View File

@@ -23,5 +23,6 @@ secrets.json
/dist
poe_device.json
/backups/
public/movingUI/
poe-error.log
poe-success.log

54
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "sillytavern",
"version": "1.8.3",
"version": "1.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sillytavern",
"version": "1.8.3",
"version": "1.9.0",
"license": "AGPL-3.0",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
@@ -71,18 +71,18 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
"integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.19.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -647,9 +647,9 @@
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="
},
"node_modules/@xmldom/xmldom": {
"version": "0.7.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.11.tgz",
"integrity": "sha512-UDi3g6Jss/W5FnSzO9jCtQwEpfymt0M+sPPlmLhDH6h2TJ8j4ESE/LpmNPBij15J5NKkk4/cg/qoVMdWI3vnlQ==",
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.9.tgz",
"integrity": "sha512-4VSbbcMoxc4KLjb1gs96SRmi7w4h1SF+fCoiK0XaQX62buCc1G5d0DC5bJ9xJBNPDSVCmIrcl8BiYxzjrqaaJA==",
"optional": true,
"engines": {
"node": ">=10.0.0"
@@ -1310,12 +1310,12 @@
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
},
"node_modules/exifreader": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.12.0.tgz",
"integrity": "sha512-aRSmNyw2c6f6qPK4jmC56W/5XePDN7LVwt8tQjgMchxoY3MCxqEToegirKdS7A3CYCWAOPehfypMZWGWxtLhzw==",
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.13.0.tgz",
"integrity": "sha512-IhJBpyXDLbCdgzVHkthadOvrMiZOR2XS7POVp0b5JoVfScRoCJ6YazZ+stTkbDTE5TRTP44bE5RKsujckAs45Q==",
"hasInstallScript": true,
"optionalDependencies": {
"@xmldom/xmldom": "^0.7.8"
"@xmldom/xmldom": "^0.8.8"
}
},
"node_modules/expand-template": {
@@ -1396,9 +1396,9 @@
]
},
"node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz",
"integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@@ -2226,9 +2226,9 @@
}
},
"node_modules/node-abi": {
"version": "3.43.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.43.0.tgz",
"integrity": "sha512-QB0MMv+tn9Ur2DtJrc8y09n0n6sw88CyDniWSX2cHW10goQXYPK9ZpFJOktDS4ron501edPX6h9i7Pg+RnH5nQ==",
"version": "3.45.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.45.0.tgz",
"integrity": "sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ==",
"dev": true,
"dependencies": {
"semver": "^7.3.5"
@@ -2238,9 +2238,9 @@
}
},
"node_modules/node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -2991,9 +2991,9 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/semver": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
"integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"

View File

@@ -49,7 +49,7 @@
"type": "git",
"url": "https://github.com/SillyTavern/SillyTavern.git"
},
"version": "1.8.3",
"version": "1.9.0",
"scripts": {
"start": "node server.js",
"pkg": "pkg --compress Gzip --no-bytecode --public ."

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 200,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": true
"early_stopping": true,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": false,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": false,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 200,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -0,0 +1,22 @@
{
"temp": 1,
"top_p": 1,
"top_k": 0,
"typical_p": 1,
"top_a": 0,
"tfs": 1,
"epsilon_cutoff": 0,
"eta_cutoff": 0,
"rep_pen": 1,
"no_repeat_ngram_size": 0,
"penalty_alpha": 0,
"num_beams": 1,
"length_penalty": 1,
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false,
"mirostat_mode": 2,
"mirostat_tau": 8,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -13,5 +13,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1.07,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -15,5 +15,8 @@
"min_length": 0,
"encoder_rep_pen": 1,
"do_sample": true,
"early_stopping": false
"early_stopping": false,
"mirostat_mode": 0,
"mirostat_tau": 5,
"mirostat_eta": 0.1
}

View File

@@ -292,7 +292,293 @@
"WEBP": "WEBP",
"presets": "预设",
"Message Sound": "消息音效",
"Author's Note": "作者的注释"
"Author's Note": "作者的注释",
"Send Jailbreak": "NEEDS TRANSLATION",
"Replace empty message": "NEEDS TRANSLATION",
"Send this text instead of nothing when the text box is empty.": "NEEDS TRANSLATION",
"NSFW avoidance prompt": "NEEDS TRANSLATION",
"Prompt that is used when the NSFW toggle is off": "NEEDS TRANSLATION",
"Advanced prompt bits": "NEEDS TRANSLATION",
"World Info format template": "NEEDS TRANSLATION",
"Wraps activated World Info entries before inserting into the prompt. Use {0} to mark a place where the content is inserted.": "NEEDS TRANSLATION",
"Unrestricted maximum value for the context slider": "NEEDS TRANSLATION",
"Chat Completion Source": "NEEDS TRANSLATION",
"Avoid sending sensitive information to the Horde.": "NEEDS TRANSLATION",
"Review the Privacy statement": "NEEDS TRANSLATION",
"Learn how to contribute your idel GPU cycles to the Horde": "NEEDS TRANSLATION",
"Trusted workers only": "NEEDS TRANSLATION",
"For privacy reasons, your API key will be hidden after you reload the page.": "NEEDS TRANSLATION",
"-- Horde models not loaded --": "NEEDS TRANSLATION",
"Example: http://127.0.0.1:5000/api ": "NEEDS TRANSLATION",
"No connection...": "NEEDS TRANSLATION",
"Get your NovelAI API Key": "NEEDS TRANSLATION",
"KoboldAI Horde": "NEEDS TRANSLATION",
"Text Gen WebUI (ooba)": "NEEDS TRANSLATION",
"NovelAI": "NEEDS TRANSLATION",
"Chat Completion (OpenAI, Claude, Window/OpenRouter, Scale)": "NEEDS TRANSLATION",
"OpenAI API key": "NEEDS TRANSLATION",
"Trim spaces": "NEEDS TRANSLATION",
"Trim Incomplete Sentences": "NEEDS TRANSLATION",
"Include Newline": "NEEDS TRANSLATION",
"Non-markdown strings": "NEEDS TRANSLATION",
"Replace Macro in Sequences": "NEEDS TRANSLATION",
"Presets": "NEEDS TRANSLATION",
"Separator": "NEEDS TRANSLATION",
"Start Reply With": "NEEDS TRANSLATION",
"Show reply prefix in chat": "NEEDS TRANSLATION",
"Worlds/Lorebooks": "NEEDS TRANSLATION",
"Active World(s)": "NEEDS TRANSLATION",
"Character Lore Insertion Strategy": "NEEDS TRANSLATION",
"Sorted Evenly": "NEEDS TRANSLATION",
"Character Lore First": "NEEDS TRANSLATION",
"Global Lore First": "NEEDS TRANSLATION",
"-- World Info not found --": "NEEDS TRANSLATION",
"Recursive Scan": "NEEDS TRANSLATION",
"Case Sensitive": "NEEDS TRANSLATION",
"Match whole words": "NEEDS TRANSLATION",
"World/Lore Editor": "NEEDS TRANSLATION",
"--- None ---": "NEEDS TRANSLATION",
"Comma seperated (ignored if empty)": "NEEDS TRANSLATION",
"Use Probability": "NEEDS TRANSLATION",
"Exclude from recursion": "NEEDS TRANSLATION",
"Position:": "NEEDS TRANSLATION",
"Before Char Defs": "NEEDS TRANSLATION",
"After Char Defs": "NEEDS TRANSLATION",
"Before AN": "NEEDS TRANSLATION",
"After AN": "NEEDS TRANSLATION",
"Order:": "NEEDS TRANSLATION",
"Probability:": "NEEDS TRANSLATION",
"Delete Entry": "NEEDS TRANSLATION",
"User Message Blur Tint": "NEEDS TRANSLATION",
"AI Message Blur Tint": "NEEDS TRANSLATION",
"Chat Style:": "NEEDS TRANSLATION",
"Chat Width (PC):": "NEEDS TRANSLATION",
"Chat Timestamps": "NEEDS TRANSLATION",
"Message IDs": "NEEDS TRANSLATION",
"Prefer Character Card Prompt": "NEEDS TRANSLATION",
"Prefer Character Card Jailbreak": "NEEDS TRANSLATION",
"Press Send to continue": "NEEDS TRANSLATION",
"Log prompts to console": "NEEDS TRANSLATION",
"Never resize avatars": "NEEDS TRANSLATION",
"Show avatar filenames": "NEEDS TRANSLATION",
"Import Card Tags": "NEEDS TRANSLATION",
"Confirm message deletion": "NEEDS TRANSLATION",
"Spoiler Free Mode": "NEEDS TRANSLATION",
"Auto-swipe": "NEEDS TRANSLATION",
"Minimum generated message length": "NEEDS TRANSLATION",
"Blacklisted words": "NEEDS TRANSLATION",
"Blacklisted word count to swipe": "NEEDS TRANSLATION",
"Reload Chat": "NEEDS TRANSLATION",
"Not Connected": "NEEDS TRANSLATION",
"Persona Management": "NEEDS TRANSLATION",
"Persona Description": "NEEDS TRANSLATION",
"Before Character Card": "NEEDS TRANSLATION",
"After Character Card": "NEEDS TRANSLATION",
"Top of Author's Note": "NEEDS TRANSLATION",
"Bottom of Author's Note": "NEEDS TRANSLATION",
"How do I use this?": "NEEDS TRANSLATION",
"More...": "NEEDS TRANSLATION",
"Link to World Info": "NEEDS TRANSLATION",
"Import Card Lore": "NEEDS TRANSLATION",
"Scenario Override": "NEEDS TRANSLATION",
"Rename": "NEEDS TRANSLATION",
"Character Description": "NEEDS TRANSLATION",
"Creator's Notes": "NEEDS TRANSLATION",
"A-Z": "NEEDS TRANSLATION",
"Z-A": "NEEDS TRANSLATION",
"Newest": "NEEDS TRANSLATION",
"Oldest": "NEEDS TRANSLATION",
"Favorites": "NEEDS TRANSLATION",
"Recent": "NEEDS TRANSLATION",
"Most chats": "NEEDS TRANSLATION",
"Least chats": "NEEDS TRANSLATION",
"Back": "NEEDS TRANSLATION",
"Prompt Overrides (For OpenAI/Claude/Scale APIs, Window/OpenRouter, Poe, and Instruct mode)": "NEEDS TRANSLATION",
"Insert {{original}} into either box to include the respective default prompt from system settings.": "NEEDS TRANSLATION",
"Main Prompt": "NEEDS TRANSLATION",
"Jailbreak": "NEEDS TRANSLATION",
"Creator's Metadata (Not sent with the AI prompt)": "NEEDS TRANSLATION",
"Everything here is optional": "NEEDS TRANSLATION",
"Created by": "NEEDS TRANSLATION",
"Character Version": "NEEDS TRANSLATION",
"Tags to Embed": "NEEDS TRANSLATION",
"How often the character speaks in group chats!": "NEEDS TRANSLATION",
"Important to set the character's writing style.": "NEEDS TRANSLATION",
"ATTENTION!": "NEEDS TRANSLATION",
"Samplers Order": "NEEDS TRANSLATION",
"Samplers will be applied in a top-down order. Use with caution.": "NEEDS TRANSLATION",
"Repetition Penalty": "NEEDS TRANSLATION",
"Epsilon Cutoff": "NEEDS TRANSLATION",
"Eta Cutoff": "NEEDS TRANSLATION",
"Rep. Pen. Range.": "NEEDS TRANSLATION",
"Rep. Pen. Freq.": "NEEDS TRANSLATION",
"Rep. Pen. Presence": "NEEDS TRANSLATION",
"Follow_poe": "NEEDS TRANSLATION",
"Enter it in the box below:": "NEEDS TRANSLATION",
"separate with commas w/o space between": "NEEDS TRANSLATION",
"Document": "NEEDS TRANSLATION",
"Suggest replies": "NEEDS TRANSLATION",
"Show suggested replies. Not all bots support this.": "NEEDS TRANSLATION",
"Use 'Unlocked Context' to enable chunked generation.": "NEEDS TRANSLATION",
"It extends the context window in exchange for reply generation speed.": "NEEDS TRANSLATION",
"Safe Context Size values for Poe bots:": "NEEDS TRANSLATION",
"ChatGPT / Sage = 3600-4000 tokens": "NEEDS TRANSLATION",
"Claude-instant / Claude+ = 5000-5500 tokens": "NEEDS TRANSLATION",
"GPT-4 = 7600-8000": "NEEDS TRANSLATION",
"Purge Poe Chat": "NEEDS TRANSLATION",
"Continue": "NEEDS TRANSLATION",
"Editing:": "NEEDS TRANSLATION",
"AI reply prefix": "NEEDS TRANSLATION",
"Custom Stopping Strings": "NEEDS TRANSLATION",
"JSON serialized array of strings": "NEEDS TRANSLATION",
"words you dont want generated separated by comma ','": "NEEDS TRANSLATION",
"Extensions URL": "NEEDS TRANSLATION",
"API Key": "NEEDS TRANSLATION",
"Enter your name": "NEEDS TRANSLATION",
"Name this character": "NEEDS TRANSLATION",
"Search / Create Tags": "NEEDS TRANSLATION",
"Describe your character's physical and mental traits here.": "NEEDS TRANSLATION",
"This will be the first message from the character that starts every chat.": "NEEDS TRANSLATION",
"Chat Name (Optional)": "NEEDS TRANSLATION",
"Filter...": "NEEDS TRANSLATION",
"Search...": "NEEDS TRANSLATION",
"Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "NEEDS TRANSLATION",
"Any contents here will replace the default Jailbreak Prompt (Character Note for Poe) used for this character. (v2 spec: post_history_instructions)": "NEEDS TRANSLATION",
"(Botmaker's name / Contact Info)": "NEEDS TRANSLATION",
"(If you want to track character versions)": "NEEDS TRANSLATION",
"(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "NEEDS TRANSLATION",
"(Write a comma-separated list of tags)": "NEEDS TRANSLATION",
"(A brief description of the personality)": "NEEDS TRANSLATION",
"(Circumstances and context of the interaction)": "NEEDS TRANSLATION",
"(Examples of chat dialog. Begin each example with START on a new line.)": "NEEDS TRANSLATION",
"Injection text (supports parameters)": "NEEDS TRANSLATION",
"Injection depth": "NEEDS TRANSLATION",
"Type here...": "NEEDS TRANSLATION",
"Comma separated (required)": "NEEDS TRANSLATION",
"Comma separated (ignored if empty)": "NEEDS TRANSLATION",
"What this keyword should mean to the AI, sent verbatim": "NEEDS TRANSLATION",
"Not sent to the AI": "NEEDS TRANSLATION",
"(This will be the first message from the character that starts every chat)": "NEEDS TRANSLATION",
"Not connected to API!": "NEEDS TRANSLATION",
"AI Response Configuration": "NEEDS TRANSLATION",
"AI Configuration panel will stay open": "NEEDS TRANSLATION",
"Update current preset": "NEEDS TRANSLATION",
"Create new preset": "NEEDS TRANSLATION",
"Import preset": "NEEDS TRANSLATION",
"Export preset": "NEEDS TRANSLATION",
"Delete the preset": "NEEDS TRANSLATION",
"Attempts to automatically jailbreak the bot": "NEEDS TRANSLATION",
"Purges the chat history on Poe site": "NEEDS TRANSLATION",
"Inserts jailbreak as a last system message": "NEEDS TRANSLATION",
"NSFW block goes first in the resulting prompt": "NEEDS TRANSLATION",
"Enables OpenAI completion streaming": "NEEDS TRANSLATION",
"Wrap user messages in quotes before sending": "NEEDS TRANSLATION",
"Restore default prompt": "NEEDS TRANSLATION",
"New preset": "NEEDS TRANSLATION",
"Delete preset": "NEEDS TRANSLATION",
"Restore default jailbreak": "NEEDS TRANSLATION",
"Restore default reply": "NEEDS TRANSLATION",
"Restore defaul note": "NEEDS TRANSLATION",
"API Connections": "NEEDS TRANSLATION",
"Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "NEEDS TRANSLATION",
"Clear your API key": "NEEDS TRANSLATION",
"Refresh models": "NEEDS TRANSLATION",
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "NEEDS TRANSLATION",
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "NEEDS TRANSLATION",
"Create New": "NEEDS TRANSLATION",
"Edit": "NEEDS TRANSLATION",
"World Info & Soft Prompts": "NEEDS TRANSLATION",
"Locked = World Editor will stay open": "NEEDS TRANSLATION",
"Entries can activate other entries by mentioning their keywords": "NEEDS TRANSLATION",
"Lookup for the entry keys in the context will respect the case": "NEEDS TRANSLATION",
"If the entry key consists of only one word, it would not be matched as part of other words": "NEEDS TRANSLATION",
"Open all Entries": "NEEDS TRANSLATION",
"Close all Entries": "NEEDS TRANSLATION",
"Create": "NEEDS TRANSLATION",
"Import World Info": "NEEDS TRANSLATION",
"Export World Info": "NEEDS TRANSLATION",
"Delete World Info": "NEEDS TRANSLATION",
"Rename World Info": "NEEDS TRANSLATION",
"Save changes to a new theme file": "NEEDS TRANSLATION",
"removes blur and uses alternative background color for divs": "NEEDS TRANSLATION",
"If checked and the character card contains a prompt override (System Prompt), use that instead.": "NEEDS TRANSLATION",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead.": "NEEDS TRANSLATION",
"AI Response Formatting": "NEEDS TRANSLATION",
"Change Background Image": "NEEDS TRANSLATION",
"Extensions": "NEEDS TRANSLATION",
"Click to set a new User Name": "NEEDS TRANSLATION",
"Click to lock your selected persona to the current chat. Click again to remove the lock.": "NEEDS TRANSLATION",
"Click to set user name for all messages": "NEEDS TRANSLATION",
"Create a dummy persona": "NEEDS TRANSLATION",
"Character Management": "NEEDS TRANSLATION",
"Locked = Character Management panel will stay open": "NEEDS TRANSLATION",
"Select/Create Characters": "NEEDS TRANSLATION",
"Token counts may be inaccurate and provided just for reference.": "NEEDS TRANSLATION",
"Click to select a new avatar for this character": "NEEDS TRANSLATION",
"Add to Favorites": "NEEDS TRANSLATION",
"Advanced Definition": "NEEDS TRANSLATION",
"Character Lore": "NEEDS TRANSLATION",
"Export and Download": "NEEDS TRANSLATION",
"Duplicate Character": "NEEDS TRANSLATION",
"Create Character": "NEEDS TRANSLATION",
"Delete Character": "NEEDS TRANSLATION",
"View all tags": "NEEDS TRANSLATION",
"Click to set additional greeting messages": "NEEDS TRANSLATION",
"Show / Hide Description and First Message": "NEEDS TRANSLATION",
"Click to select a new avatar for this group": "NEEDS TRANSLATION",
"Set a group chat scenario": "NEEDS TRANSLATION",
"Restore collage avatar": "NEEDS TRANSLATION",
"Create New Character": "NEEDS TRANSLATION",
"Import Character from File": "NEEDS TRANSLATION",
"Import content from external URL": "NEEDS TRANSLATION",
"Create New Chat Group": "NEEDS TRANSLATION",
"Characters sorting order": "NEEDS TRANSLATION",
"Add chat injection": "NEEDS TRANSLATION",
"Remove injection": "NEEDS TRANSLATION",
"Remove": "NEEDS TRANSLATION",
"Select a World Info file for": "NEEDS TRANSLATION",
"Primary Lorebook": "NEEDS TRANSLATION",
"A selected World Info will be bound to this character as its own Lorebook.": "NEEDS TRANSLATION",
"When generating an AI reply, it will be combined with the entries from a global World Info selector.": "NEEDS TRANSLATION",
"Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "NEEDS TRANSLATION",
"Additional Lorebooks": "NEEDS TRANSLATION",
"Associate one or more auxillary Lorebooks with this character.": "NEEDS TRANSLATION",
"NOTE: These choices are optional and won't be preserved on character export!": "NEEDS TRANSLATION",
"Rename chat file": "NEEDS TRANSLATION",
"Export JSONL chat file": "NEEDS TRANSLATION",
"Download chat as plain text document": "NEEDS TRANSLATION",
"Delete chat file": "NEEDS TRANSLATION",
"Delete tag": "NEEDS TRANSLATION",
"Translate message": "NEEDS TRANSLATION",
"Generate Image": "NEEDS TRANSLATION",
"Narrate": "NEEDS TRANSLATION",
"Prompt": "NEEDS TRANSLATION",
"Create Bookmark": "NEEDS TRANSLATION",
"Copy": "NEEDS TRANSLATION",
"Open bookmark chat": "NEEDS TRANSLATION",
"Confirm": "NEEDS TRANSLATION",
"Copy this message": "NEEDS TRANSLATION",
"Delete this message": "NEEDS TRANSLATION",
"Move message up": "NEEDS TRANSLATION",
"Move message down": "NEEDS TRANSLATION",
"Enlarge": "NEEDS TRANSLATION",
"Temporarily disable automatic replies from this character": "NEEDS TRANSLATION",
"Enable automatic replies from this character": "NEEDS TRANSLATION",
"Trigger a message from this character": "NEEDS TRANSLATION",
"Move up": "NEEDS TRANSLATION",
"Move down": "NEEDS TRANSLATION",
"View character card": "NEEDS TRANSLATION",
"Remove from group": "NEEDS TRANSLATION",
"Add to group": "NEEDS TRANSLATION",
"Add": "NEEDS TRANSLATION",
"Abort request": "NEEDS TRANSLATION",
"Send a message": "NEEDS TRANSLATION",
"Ask AI to write your message for you": "NEEDS TRANSLATION",
"Continue the last message": "NEEDS TRANSLATION",
"Bind user name to that avatar": "NEEDS TRANSLATION",
"Select this as default persona for the new chats.": "NEEDS TRANSLATION",
"Change persona image": "NEEDS TRANSLATION",
"Delete persona": "NEEDS TRANSLATION"
},
"ja-jp": {
"clickslidertips": "スライダーの右側の数字をクリックすると手動で入力できます。",
@@ -503,7 +789,6 @@
"Auto-save Message Edits": "メッセージの編集を自動保存",
"Auto-fix Markdown": "Markdownを自動修正",
"Allow : in bot messages": "ボットメッセージで「:」を許可する",
"Allow : in bot messages": "ボットメッセージで「:」を許可する",
"Auto-scroll Chat": "チャット自動スクロール",
"Render Formulas": "数式のレンダリング",
"Send on Enter": "エンター入力で送信",
@@ -581,7 +866,293 @@
"WEBP": "WEBP",
"presets": "プリセット",
"Message Sound": "メッセージ音",
"Author's Note": "作者の注記"
"Author's Note": "作者の注記",
"Send Jailbreak": "NEEDS TRANSLATION",
"Replace empty message": "NEEDS TRANSLATION",
"Send this text instead of nothing when the text box is empty.": "NEEDS TRANSLATION",
"NSFW avoidance prompt": "NEEDS TRANSLATION",
"Prompt that is used when the NSFW toggle is off": "NEEDS TRANSLATION",
"Advanced prompt bits": "NEEDS TRANSLATION",
"World Info format template": "NEEDS TRANSLATION",
"Wraps activated World Info entries before inserting into the prompt. Use {0} to mark a place where the content is inserted.": "NEEDS TRANSLATION",
"Unrestricted maximum value for the context slider": "NEEDS TRANSLATION",
"Chat Completion Source": "NEEDS TRANSLATION",
"Avoid sending sensitive information to the Horde.": "NEEDS TRANSLATION",
"Review the Privacy statement": "NEEDS TRANSLATION",
"Learn how to contribute your idel GPU cycles to the Horde": "NEEDS TRANSLATION",
"Trusted workers only": "NEEDS TRANSLATION",
"For privacy reasons, your API key will be hidden after you reload the page.": "NEEDS TRANSLATION",
"-- Horde models not loaded --": "NEEDS TRANSLATION",
"Example: http://127.0.0.1:5000/api ": "NEEDS TRANSLATION",
"No connection...": "NEEDS TRANSLATION",
"Get your NovelAI API Key": "NEEDS TRANSLATION",
"KoboldAI Horde": "NEEDS TRANSLATION",
"Text Gen WebUI (ooba)": "NEEDS TRANSLATION",
"NovelAI": "NEEDS TRANSLATION",
"Chat Completion (OpenAI, Claude, Window/OpenRouter, Scale)": "NEEDS TRANSLATION",
"OpenAI API key": "NEEDS TRANSLATION",
"Trim spaces": "NEEDS TRANSLATION",
"Trim Incomplete Sentences": "NEEDS TRANSLATION",
"Include Newline": "NEEDS TRANSLATION",
"Non-markdown strings": "NEEDS TRANSLATION",
"Replace Macro in Sequences": "NEEDS TRANSLATION",
"Presets": "NEEDS TRANSLATION",
"Separator": "NEEDS TRANSLATION",
"Start Reply With": "NEEDS TRANSLATION",
"Show reply prefix in chat": "NEEDS TRANSLATION",
"Worlds/Lorebooks": "NEEDS TRANSLATION",
"Active World(s)": "NEEDS TRANSLATION",
"Character Lore Insertion Strategy": "NEEDS TRANSLATION",
"Sorted Evenly": "NEEDS TRANSLATION",
"Character Lore First": "NEEDS TRANSLATION",
"Global Lore First": "NEEDS TRANSLATION",
"-- World Info not found --": "NEEDS TRANSLATION",
"Recursive Scan": "NEEDS TRANSLATION",
"Case Sensitive": "NEEDS TRANSLATION",
"Match whole words": "NEEDS TRANSLATION",
"World/Lore Editor": "NEEDS TRANSLATION",
"--- None ---": "NEEDS TRANSLATION",
"Comma seperated (ignored if empty)": "NEEDS TRANSLATION",
"Use Probability": "NEEDS TRANSLATION",
"Exclude from recursion": "NEEDS TRANSLATION",
"Position:": "NEEDS TRANSLATION",
"Before Char Defs": "NEEDS TRANSLATION",
"After Char Defs": "NEEDS TRANSLATION",
"Before AN": "NEEDS TRANSLATION",
"After AN": "NEEDS TRANSLATION",
"Order:": "NEEDS TRANSLATION",
"Probability:": "NEEDS TRANSLATION",
"Delete Entry": "NEEDS TRANSLATION",
"User Message Blur Tint": "NEEDS TRANSLATION",
"AI Message Blur Tint": "NEEDS TRANSLATION",
"Chat Style:": "NEEDS TRANSLATION",
"Chat Width (PC):": "NEEDS TRANSLATION",
"Chat Timestamps": "NEEDS TRANSLATION",
"Message IDs": "NEEDS TRANSLATION",
"Prefer Character Card Prompt": "NEEDS TRANSLATION",
"Prefer Character Card Jailbreak": "NEEDS TRANSLATION",
"Press Send to continue": "NEEDS TRANSLATION",
"Log prompts to console": "NEEDS TRANSLATION",
"Never resize avatars": "NEEDS TRANSLATION",
"Show avatar filenames": "NEEDS TRANSLATION",
"Import Card Tags": "NEEDS TRANSLATION",
"Confirm message deletion": "NEEDS TRANSLATION",
"Spoiler Free Mode": "NEEDS TRANSLATION",
"Auto-swipe": "NEEDS TRANSLATION",
"Minimum generated message length": "NEEDS TRANSLATION",
"Blacklisted words": "NEEDS TRANSLATION",
"Blacklisted word count to swipe": "NEEDS TRANSLATION",
"Reload Chat": "NEEDS TRANSLATION",
"Not Connected": "NEEDS TRANSLATION",
"Persona Management": "NEEDS TRANSLATION",
"Persona Description": "NEEDS TRANSLATION",
"Before Character Card": "NEEDS TRANSLATION",
"After Character Card": "NEEDS TRANSLATION",
"Top of Author's Note": "NEEDS TRANSLATION",
"Bottom of Author's Note": "NEEDS TRANSLATION",
"How do I use this?": "NEEDS TRANSLATION",
"More...": "NEEDS TRANSLATION",
"Link to World Info": "NEEDS TRANSLATION",
"Import Card Lore": "NEEDS TRANSLATION",
"Scenario Override": "NEEDS TRANSLATION",
"Rename": "NEEDS TRANSLATION",
"Character Description": "NEEDS TRANSLATION",
"Creator's Notes": "NEEDS TRANSLATION",
"A-Z": "NEEDS TRANSLATION",
"Z-A": "NEEDS TRANSLATION",
"Newest": "NEEDS TRANSLATION",
"Oldest": "NEEDS TRANSLATION",
"Favorites": "NEEDS TRANSLATION",
"Recent": "NEEDS TRANSLATION",
"Most chats": "NEEDS TRANSLATION",
"Least chats": "NEEDS TRANSLATION",
"Back": "NEEDS TRANSLATION",
"Prompt Overrides (For OpenAI/Claude/Scale APIs, Window/OpenRouter, Poe, and Instruct mode)": "NEEDS TRANSLATION",
"Insert {{original}} into either box to include the respective default prompt from system settings.": "NEEDS TRANSLATION",
"Main Prompt": "NEEDS TRANSLATION",
"Jailbreak": "NEEDS TRANSLATION",
"Creator's Metadata (Not sent with the AI prompt)": "NEEDS TRANSLATION",
"Everything here is optional": "NEEDS TRANSLATION",
"Created by": "NEEDS TRANSLATION",
"Character Version": "NEEDS TRANSLATION",
"Tags to Embed": "NEEDS TRANSLATION",
"How often the character speaks in group chats!": "NEEDS TRANSLATION",
"Important to set the character's writing style.": "NEEDS TRANSLATION",
"ATTENTION!": "NEEDS TRANSLATION",
"Samplers Order": "NEEDS TRANSLATION",
"Samplers will be applied in a top-down order. Use with caution.": "NEEDS TRANSLATION",
"Repetition Penalty": "NEEDS TRANSLATION",
"Epsilon Cutoff": "NEEDS TRANSLATION",
"Eta Cutoff": "NEEDS TRANSLATION",
"Rep. Pen. Range.": "NEEDS TRANSLATION",
"Rep. Pen. Freq.": "NEEDS TRANSLATION",
"Rep. Pen. Presence": "NEEDS TRANSLATION",
"Follow_poe": "NEEDS TRANSLATION",
"Enter it in the box below:": "NEEDS TRANSLATION",
"separate with commas w/o space between": "NEEDS TRANSLATION",
"Document": "NEEDS TRANSLATION",
"Suggest replies": "NEEDS TRANSLATION",
"Show suggested replies. Not all bots support this.": "NEEDS TRANSLATION",
"Use 'Unlocked Context' to enable chunked generation.": "NEEDS TRANSLATION",
"It extends the context window in exchange for reply generation speed.": "NEEDS TRANSLATION",
"Safe Context Size values for Poe bots:": "NEEDS TRANSLATION",
"ChatGPT / Sage = 3600-4000 tokens": "NEEDS TRANSLATION",
"Claude-instant / Claude+ = 5000-5500 tokens": "NEEDS TRANSLATION",
"GPT-4 = 7600-8000": "NEEDS TRANSLATION",
"Purge Poe Chat": "NEEDS TRANSLATION",
"Continue": "NEEDS TRANSLATION",
"Editing:": "NEEDS TRANSLATION",
"AI reply prefix": "NEEDS TRANSLATION",
"Custom Stopping Strings": "NEEDS TRANSLATION",
"JSON serialized array of strings": "NEEDS TRANSLATION",
"words you dont want generated separated by comma ','": "NEEDS TRANSLATION",
"Extensions URL": "NEEDS TRANSLATION",
"API Key": "NEEDS TRANSLATION",
"Enter your name": "NEEDS TRANSLATION",
"Name this character": "NEEDS TRANSLATION",
"Search / Create Tags": "NEEDS TRANSLATION",
"Describe your character's physical and mental traits here.": "NEEDS TRANSLATION",
"This will be the first message from the character that starts every chat.": "NEEDS TRANSLATION",
"Chat Name (Optional)": "NEEDS TRANSLATION",
"Filter...": "NEEDS TRANSLATION",
"Search...": "NEEDS TRANSLATION",
"Any contents here will replace the default Main Prompt used for this character. (v2 spec: system_prompt)": "NEEDS TRANSLATION",
"Any contents here will replace the default Jailbreak Prompt (Character Note for Poe) used for this character. (v2 spec: post_history_instructions)": "NEEDS TRANSLATION",
"(Botmaker's name / Contact Info)": "NEEDS TRANSLATION",
"(If you want to track character versions)": "NEEDS TRANSLATION",
"(Describe the bot, give use tips, or list the chat models it has been tested on. This will be displayed in the character list.)": "NEEDS TRANSLATION",
"(Write a comma-separated list of tags)": "NEEDS TRANSLATION",
"(A brief description of the personality)": "NEEDS TRANSLATION",
"(Circumstances and context of the interaction)": "NEEDS TRANSLATION",
"(Examples of chat dialog. Begin each example with START on a new line.)": "NEEDS TRANSLATION",
"Injection text (supports parameters)": "NEEDS TRANSLATION",
"Injection depth": "NEEDS TRANSLATION",
"Type here...": "NEEDS TRANSLATION",
"Comma separated (required)": "NEEDS TRANSLATION",
"Comma separated (ignored if empty)": "NEEDS TRANSLATION",
"What this keyword should mean to the AI, sent verbatim": "NEEDS TRANSLATION",
"Not sent to the AI": "NEEDS TRANSLATION",
"(This will be the first message from the character that starts every chat)": "NEEDS TRANSLATION",
"Not connected to API!": "NEEDS TRANSLATION",
"AI Response Configuration": "NEEDS TRANSLATION",
"AI Configuration panel will stay open": "NEEDS TRANSLATION",
"Update current preset": "NEEDS TRANSLATION",
"Create new preset": "NEEDS TRANSLATION",
"Import preset": "NEEDS TRANSLATION",
"Export preset": "NEEDS TRANSLATION",
"Delete the preset": "NEEDS TRANSLATION",
"Attempts to automatically jailbreak the bot": "NEEDS TRANSLATION",
"Purges the chat history on Poe site": "NEEDS TRANSLATION",
"Inserts jailbreak as a last system message": "NEEDS TRANSLATION",
"NSFW block goes first in the resulting prompt": "NEEDS TRANSLATION",
"Enables OpenAI completion streaming": "NEEDS TRANSLATION",
"Wrap user messages in quotes before sending": "NEEDS TRANSLATION",
"Restore default prompt": "NEEDS TRANSLATION",
"New preset": "NEEDS TRANSLATION",
"Delete preset": "NEEDS TRANSLATION",
"Restore default jailbreak": "NEEDS TRANSLATION",
"Restore default reply": "NEEDS TRANSLATION",
"Restore defaul note": "NEEDS TRANSLATION",
"API Connections": "NEEDS TRANSLATION",
"Can help with bad responses by queueing only the approved workers. May slowdown the response time.": "NEEDS TRANSLATION",
"Clear your API key": "NEEDS TRANSLATION",
"Refresh models": "NEEDS TRANSLATION",
"Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "NEEDS TRANSLATION",
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "NEEDS TRANSLATION",
"Create New": "NEEDS TRANSLATION",
"Edit": "NEEDS TRANSLATION",
"World Info & Soft Prompts": "NEEDS TRANSLATION",
"Locked = World Editor will stay open": "NEEDS TRANSLATION",
"Entries can activate other entries by mentioning their keywords": "NEEDS TRANSLATION",
"Lookup for the entry keys in the context will respect the case": "NEEDS TRANSLATION",
"If the entry key consists of only one word, it would not be matched as part of other words": "NEEDS TRANSLATION",
"Open all Entries": "NEEDS TRANSLATION",
"Close all Entries": "NEEDS TRANSLATION",
"Create": "NEEDS TRANSLATION",
"Import World Info": "NEEDS TRANSLATION",
"Export World Info": "NEEDS TRANSLATION",
"Delete World Info": "NEEDS TRANSLATION",
"Rename World Info": "NEEDS TRANSLATION",
"Save changes to a new theme file": "NEEDS TRANSLATION",
"removes blur and uses alternative background color for divs": "NEEDS TRANSLATION",
"If checked and the character card contains a prompt override (System Prompt), use that instead.": "NEEDS TRANSLATION",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead.": "NEEDS TRANSLATION",
"AI Response Formatting": "NEEDS TRANSLATION",
"Change Background Image": "NEEDS TRANSLATION",
"Extensions": "NEEDS TRANSLATION",
"Click to set a new User Name": "NEEDS TRANSLATION",
"Click to lock your selected persona to the current chat. Click again to remove the lock.": "NEEDS TRANSLATION",
"Click to set user name for all messages": "NEEDS TRANSLATION",
"Create a dummy persona": "NEEDS TRANSLATION",
"Character Management": "NEEDS TRANSLATION",
"Locked = Character Management panel will stay open": "NEEDS TRANSLATION",
"Select/Create Characters": "NEEDS TRANSLATION",
"Token counts may be inaccurate and provided just for reference.": "NEEDS TRANSLATION",
"Click to select a new avatar for this character": "NEEDS TRANSLATION",
"Add to Favorites": "NEEDS TRANSLATION",
"Advanced Definition": "NEEDS TRANSLATION",
"Character Lore": "NEEDS TRANSLATION",
"Export and Download": "NEEDS TRANSLATION",
"Duplicate Character": "NEEDS TRANSLATION",
"Create Character": "NEEDS TRANSLATION",
"Delete Character": "NEEDS TRANSLATION",
"View all tags": "NEEDS TRANSLATION",
"Click to set additional greeting messages": "NEEDS TRANSLATION",
"Show / Hide Description and First Message": "NEEDS TRANSLATION",
"Click to select a new avatar for this group": "NEEDS TRANSLATION",
"Set a group chat scenario": "NEEDS TRANSLATION",
"Restore collage avatar": "NEEDS TRANSLATION",
"Create New Character": "NEEDS TRANSLATION",
"Import Character from File": "NEEDS TRANSLATION",
"Import content from external URL": "NEEDS TRANSLATION",
"Create New Chat Group": "NEEDS TRANSLATION",
"Characters sorting order": "NEEDS TRANSLATION",
"Add chat injection": "NEEDS TRANSLATION",
"Remove injection": "NEEDS TRANSLATION",
"Remove": "NEEDS TRANSLATION",
"Select a World Info file for": "NEEDS TRANSLATION",
"Primary Lorebook": "NEEDS TRANSLATION",
"A selected World Info will be bound to this character as its own Lorebook.": "NEEDS TRANSLATION",
"When generating an AI reply, it will be combined with the entries from a global World Info selector.": "NEEDS TRANSLATION",
"Exporting a character would also export the selected Lorebook file embedded in the JSON data.": "NEEDS TRANSLATION",
"Additional Lorebooks": "NEEDS TRANSLATION",
"Associate one or more auxillary Lorebooks with this character.": "NEEDS TRANSLATION",
"NOTE: These choices are optional and won't be preserved on character export!": "NEEDS TRANSLATION",
"Rename chat file": "NEEDS TRANSLATION",
"Export JSONL chat file": "NEEDS TRANSLATION",
"Download chat as plain text document": "NEEDS TRANSLATION",
"Delete chat file": "NEEDS TRANSLATION",
"Delete tag": "NEEDS TRANSLATION",
"Translate message": "NEEDS TRANSLATION",
"Generate Image": "NEEDS TRANSLATION",
"Narrate": "NEEDS TRANSLATION",
"Prompt": "NEEDS TRANSLATION",
"Create Bookmark": "NEEDS TRANSLATION",
"Copy": "NEEDS TRANSLATION",
"Open bookmark chat": "NEEDS TRANSLATION",
"Confirm": "NEEDS TRANSLATION",
"Copy this message": "NEEDS TRANSLATION",
"Delete this message": "NEEDS TRANSLATION",
"Move message up": "NEEDS TRANSLATION",
"Move message down": "NEEDS TRANSLATION",
"Enlarge": "NEEDS TRANSLATION",
"Temporarily disable automatic replies from this character": "NEEDS TRANSLATION",
"Enable automatic replies from this character": "NEEDS TRANSLATION",
"Trigger a message from this character": "NEEDS TRANSLATION",
"Move up": "NEEDS TRANSLATION",
"Move down": "NEEDS TRANSLATION",
"View character card": "NEEDS TRANSLATION",
"Remove from group": "NEEDS TRANSLATION",
"Add to group": "NEEDS TRANSLATION",
"Add": "NEEDS TRANSLATION",
"Abort request": "NEEDS TRANSLATION",
"Send a message": "NEEDS TRANSLATION",
"Ask AI to write your message for you": "NEEDS TRANSLATION",
"Continue the last message": "NEEDS TRANSLATION",
"Bind user name to that avatar": "NEEDS TRANSLATION",
"Select this as default persona for the new chats.": "NEEDS TRANSLATION",
"Change persona image": "NEEDS TRANSLATION",
"Delete persona": "NEEDS TRANSLATION"
},
"ko-kr": {
"clickslidertips": "슬라이더의 오른쪽 숫자를 클릭하면 직접 입력할 수 있습니다.",
@@ -1160,4 +1731,4 @@
"Change persona image": "주인공 아바타 바꾸기",
"Delete persona": "주인공 삭제하기"
}
}
}

View File

@@ -93,6 +93,7 @@
<script type="module" src="scripts/secrets.js"></script>
<script type="module" src="scripts/context-template.js"></script>
<script type="module" src="scripts/extensions.js"></script>
<script type="module" src="scripts/authors-note.js"></script>
<script type="text/javascript" src="scripts/toolcool-color-picker.js"></script>
<title>SillyTavern</title>
@@ -1033,6 +1034,54 @@
</div>
</div>
</div>
<hr>
<h4 data-i18n="Mirostat (mode=1 is only for llama.cpp)">Mirostat (mode=1 is only for llama.cpp)</h4>
<div class="range-block">
<div class="range-block-title" data-i18n="Mirostat Mode">
Mirostat Mode
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="mirostat_mode_textgenerationwebui" name="volume" min="0" max="2" step="1" />
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="mirostat_mode_textgenerationwebui" id="mirostat_mode_counter_textgenerationwebui">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Mirostat Tau">
Mirostat Tau
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="mirostat_tau_textgenerationwebui" name="volume" min="0" max="10" step="0.01" />
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="mirostat_tau_textgenerationwebui" id="mirostat_tau_counter_textgenerationwebui">
select
</div>
</div>
</div>
</div>
<div class="range-block">
<div class="range-block-title" data-i18n="Mirostat Eta">
Mirostat Eta
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input type="range" id="mirostat_eta_textgenerationwebui" name="volume" min="0" max="1" step="0.01" />
</div>
<div class="range-block-counter">
<div contenteditable="true" data-for="mirostat_eta_textgenerationwebui" id="mirostat_eta_counter_textgenerationwebui">
select
</div>
</div>
</div>
</div>
<hr>
<div class="range-block">
<div class="range-block-title" data-i18n="Seed">
Seed
@@ -2246,12 +2295,42 @@
</div>
</div>
</div>
<div id="MovingUI-presets-block" class="flex-container flexFlowColumn">
<h4>
<span data-i18n="MovingUI Preset">MovingUI Preset</span>
</h4>
<div class="flex-container flexnowrap alignitemscenter">
<select id="movingUIPresets" class="margin0 margin-r5">
</select>
<div id="movingui-preset-save-button" title="Save changes to a new MovingUI preset file" data-i18n="[title]Save movingUI changes to a new file" class="menu_button padding5 margin0">
<i class="fa-solid fa-save"></i>
</div>
</div>
<div id="movingUIreset" class="menu_button whitespacenowrap" data-i18n="Reset Panels">
Reset MovingUI
</div>
</div>
</div>
</div>
<div name="UI Customization" class="flex-container drawer33pWidth">
<div class="ui-settings">
<h4><span data-i18n="UI Customization">UI Customization</span></h4>
<div class="range-block">
<div class="range-block-title" data-i18n="Chat Width (PC)">
Chat Width (PC)
</div>
<div class="range-block-range-and-counter">
<div class="range-block-range">
<input id="chat_width_slider" class="wide100p" type="range" min="25" max="75" step="1" value="50">
<div class="slider_hint">
<span>25%</span>
<span>50%</span>
<span>75%</span>
</div>
</div>
</div>
</div>
<div>
<span data-i18n="Avatar Style">Avatar Style:</span><br>
<label>
@@ -2271,17 +2350,6 @@
<option value="2" data-i18n="Document">Single Document</option>
</select>
</div>
<!-- <div id="sheldWidthToggleBlock">
<span data-i18n="Chat Width (PC):">Chat Width (PC):</span><br>
<label>
<input name="sheld_width" type="radio" value="0" />
800px
</label>
<label>
<input name="sheld_width" type="radio" value="1" />
1000px
</label>
</div> -->
<div>
<label for="play_message_sound" class="checkbox_label">
<input id="play_message_sound" type="checkbox" />
@@ -2351,8 +2419,7 @@
</select>
</div>
<div id="movingUIreset" class="menu_button whitespacenowrap" data-i18n="Reset Panels">
Reset Panels</div>
</div>
</div>
@@ -2864,6 +2931,7 @@
<option data-field="date_last_chat" data-order="desc" data-i18n="Recent">Recent</option>
<option data-field="chat_size" data-order="desc" data-i18n="Most chats">Most chats</option>
<option data-field="chat_size" data-order="asc" data-i18n="Least chats">Least chats</option>
<option data-field="name" data-order="random" data-i18n="Random">Random</option>
</select>
</form>
<div class="rm_tag_controls">
@@ -2995,7 +3063,7 @@
<h5 data-i18n="How often the character speaks in group chats!">How often the character speaks in &nbsp;<span class="warning">group chats!</span>
</h5>
<input id="talkativeness_slider" name="talkativeness" type="range" min="0" max="1" step="0.05" value="0.5" form="form_create">
<div id="talkativeness_hint">
<div class="slider_hint">
<span data-i18n="Shy">Shy</span>
<span data-i18n="Normal">Normal</span>
<span data-i18n="Chatty">Chatty</span>
@@ -3041,6 +3109,15 @@
</div>
</div>
<div id="background_template" class="template_element">
<div class="bg_example flex-container" bgfile="" class="bg_example_img" title="">
<div title="Rename background" bgfile="" class="bg_button bg_example_edit fa-solid fa-pencil"></div>
<div title="Delete background" bgfile="" class="bg_button bg_example_cross fa-solid fa-circle-xmark"></div>
<div class="BGSampleTitle">
</div>
</div>
</div>
<!-- templates for JS to reuse when needed -->
<div id="context_editor_template" class="template_element">
<div class="context_editor">
@@ -3434,7 +3511,7 @@
<div class="mes_edit_buttons">
<div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>
<div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div>
<div class="mes_edit_delete menu_button fa-solid fa-scissors" title="Delete this message" data-i18n="[title]Delete this message">
<div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message">
</div>
<div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div>
<div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down">
@@ -3556,7 +3633,101 @@
<div id="typing_indicator_template" class="template_element">
<div class="typing_indicator"><span class="typing_indicator_name">CHAR</span> is typing</div>
</div>
<div id="movingDivs"></div>
<div id="movingDivs">
<div id="floatingPrompt" class="drawer-content flexGap5">
<div class="panelControlBar flex-container">
<div id="floatingPromptheader" class="fa-solid fa-grip drag-grabber"></div>
<div id="ANClose" class="fa-solid fa-circle-xmark"></div>
</div>
<div name="floatingPromptHolder">
<div class="inline-drawer">
<div id="ANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>
<b>Unique to this chat</b>.<br>
Bookmarks inherit the Note from their parent, and can be changed individually after that.<br>
</small>
<textarea id="extension_floating_prompt" class="text_pole" rows="8" maxlength="10000"></textarea>
<div class="extension_token_counter">
Tokens: <span id="extension_floating_prompt_token_counter">0</span>
</div>
<div class="floating_prompt_radio_group">
<label>
<input type="radio" name="extension_floating_position" value="0" />
After scenario
</label>
<label>
<input type="radio" name="extension_floating_position" value="1" />
In-chat @ Depth <input id="extension_floating_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
</label>
</div>
<!--<label for="extension_floating_interval">In-Chat Insertion Depth</label>-->
<label for="extension_floating_interval">Insertion Frequency</label>
<input id="extension_floating_interval" class="text_pole widthUnset" type="number" min="0" max="999" /><small> (0 = Disable, 1 = Always)</small>
<br>
<span>User inputs until next insertion: <span id="extension_floating_counter">(disabled)</span></span>
</div>
</div>
<hr class="sysHR">
<div class="inline-drawer">
<div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Character Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>Will be automatically added as the author's note for this character. Will be used in groups, but
can't be modified when a group chat is open.</small>
<textarea id="extension_floating_chara" class="text_pole" rows="8" maxlength="10000" placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
<div class="extension_token_counter">
Tokens: <span id="extension_floating_chara_token_counter">0</span>
</div>
<label class="checkbox_label" for="extension_use_floating_chara">
<input id="extension_use_floating_chara" type="checkbox" />
<span data-i18n="Use character author's note">Use character author's note</span>
</label>
<div class="floating_prompt_radio_group">
<label>
<input type="radio" name="extension_floating_char_position" value="0" />
Replace Author's Note
</label>
<label>
<input type="radio" name="extension_floating_char_position" value="1" />
Top of Author's Note
</label>
<label>
<input type="radio" name="extension_floating_char_position" value="2" />
Bottom of Author's Note
</label>
</div>
</div>
</div>
<hr class="sysHR">
<div class="inline-drawer">
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Default Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>Will be automatically added as the Author's Note for all new chats.</small>
<textarea id="extension_floating_default" class="text_pole" rows="8" maxlength="10000" placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
<div class="extension_token_counter">
Tokens: <span id="extension_floating_default_token_counter">0</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="sheld">
<div id="sheldheader" class="fa-solid fa-grip drag-grabber"></div>
<div id="chat">
@@ -3582,6 +3753,10 @@
<!-- popups live outside sheld to avoid blur conflicts -->
<div id="options" class="font-family-reset" style="display: none;">
<div class="options-content">
<a id="option_toggle_AN">
<i class="fa-lg fa-solid fa-note-sticky"></i>
<span data-i18n="Author's Note">Author's Note</span>
</a>
<a id="option_back_to_main">
<i class="fa-lg fa-solid fa-left-long"></i>
<span data-i18n="Back to parent chat">Back to parent chat</span>

View File

@@ -0,0 +1,45 @@
{
"name": "Black Magic Time",
"movingUIState": {
"sheld": {
"top": 488,
"left": 1407,
"right": 1,
"bottom": 4,
"margin": "unset",
"width": 471,
"height": 439
},
"floatingPrompt": {
"width": 369,
"height": 441
},
"right-nav-panel": {
"top": 0,
"left": 1400,
"right": 111,
"bottom": 446,
"margin": "unset",
"width": 479,
"height": 487
},
"WorldInfo": {
"top": 41,
"left": 369,
"right": 642,
"bottom": 51,
"margin": "unset",
"width": 1034,
"height": 858
},
"left-nav-panel": {
"top": 442,
"left": 0,
"right": 1546,
"bottom": 25,
"margin": "unset",
"width": 368,
"height": 483
}
}
}

View File

@@ -0,0 +1,4 @@
{
"name": "Default",
"movingUIState": {}
}

View File

@@ -166,7 +166,7 @@ import {
import { EventEmitter } from './scripts/eventemitter.js';
import { context_settings, loadContextTemplatesFromSettings } from "./scripts/context-template.js";
import { markdownExclusionExt } from "./scripts/showdown-exclusion.js";
import { NOTE_MODULE_NAME, metadata_keys, setFloatingPrompt, shouldWIAddPrompt } from "./scripts/extensions/floating-prompt/index.js";
import { NOTE_MODULE_NAME, metadata_keys, setFloatingPrompt, shouldWIAddPrompt } from "./scripts/authors-note.js";
import { deviceInfo } from "./scripts/RossAscends-mods.js";
import { getRegexedString, regex_placement } from "./scripts/extensions/regex/engine.js";
@@ -365,7 +365,7 @@ const system_messages = {
is_user: false,
is_system: true,
is_name: true,
mes: [
mes:
`Hello there! Please select the help topic you would like to learn more about:
<ul>
<li><a href="javascript:displayHelp('1')">Slash Commands</a> (or <tt>/help slash</tt>)</li>
@@ -374,7 +374,6 @@ const system_messages = {
<li><a href="javascript:displayHelp('4')">{{Macros}}</a> (or <tt>/help macros</tt>)</li>
</ul>
<br><b>Still got questions left? The <a target="_blank" href="https://docs.sillytavern.app/">Official SillyTavern Documentation Website</a> has much more information!</b>`
]
},
slash_commands: {
name: systemUserName,
@@ -390,7 +389,7 @@ const system_messages = {
is_user: false,
is_system: true,
is_name: true,
mes: [
mes:
`Hotkeys/Keybinds:
<ul>
<li><tt>Up</tt> = Edit last message in chat</li>
@@ -404,7 +403,6 @@ const system_messages = {
<li><tt>Ctrl+Shift+Up</tt> = Scroll to context line</li>
<li><tt>Ctrl+Shift+Down</tt> = Scroll chat to bottom</li>
</ul>`
]
},
formatting: {
name: systemUserName,
@@ -412,7 +410,7 @@ const system_messages = {
is_user: false,
is_system: true,
is_name: true,
mes: [
mes:
`Text formatting commands:
<ul>
<li><tt>{{text}}</tt> - sets a one-time behavioral bias for the AI. Resets when you send the next message.</li>
@@ -424,7 +422,6 @@ const system_messages = {
<li><tt>$$ text $$</tt> - renders a LaTeX formula (if enabled)</li>
<li><tt>$ text $</tt> - renders an AsciiMath formula (if enabled)</li>
</ul>`
]
},
macros: {
name: systemUserName,
@@ -432,7 +429,7 @@ const system_messages = {
is_user: false,
is_system: true,
is_name: true,
mes: [
mes:
`System-wide Replacement Macros:
<ul>
<li><tt>{{user}}</tt> - your current Persona username</li>
@@ -443,7 +440,6 @@ const system_messages = {
<li><tt>{{idle_duration}}</tt> - the time since the last user message was sent</li>
<li><tt>{{random:(args)}}</tt> - returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.</li>
</ul>`
]
},
welcome:
{
@@ -554,6 +550,10 @@ async function getClientVersion() {
}
function getTokenCount(str, padding = undefined) {
if (typeof str !== 'string') {
return 0;
}
let tokenizerType = power_user.tokenizer;
if (main_api === 'openai') {
@@ -971,22 +971,25 @@ async function getBackgrounds() {
const getData = await response.json();
//background = getData;
//console.log(getData.length);
$("#bg_menu_content").empty();
for (const bg of getData) {
const thumbPath = getThumbnailUrl('bg', bg);
$("#bg_menu_content").append(
`<div class="bg_example flex-container" bgfile="${bg}" class="bg_example_img" title="${bg}" style="background-image: url('${thumbPath}');">
<div bgfile="${bg}" class="bg_example_cross fa-solid fa-circle-xmark"></div>
<div class="BGSampleTitle">
${bg
.replace('.png', '')
.replace('.jpg', '')
.replace('.webp', '')}
</div>
</div>`
);
const template = getBackgroundFromTemplate(bg);
$("#bg_menu_content").append(template);
}
}
}
function getBackgroundFromTemplate(bg) {
const thumbPath = getThumbnailUrl('bg', bg);
const template = $('#background_template .bg_example').clone();
template.attr('bgfile', bg);
template.attr('title', bg);
template.find('.bg_button').attr('bgfile', bg);
template.css('background-image', `url('${thumbPath}')`);
template.find('.BGSampleTitle').text(bg.slice(0, bg.lastIndexOf('.')));
return template;
}
async function isColab() {
is_checked_colab = true;
const response = await fetch("/iscolab", {
@@ -1110,6 +1113,7 @@ function clearChat() {
$('.zoomed_avatar[forChar]').remove();
} else { console.debug('saw no avatars') }
itemizedPrompts = [];
chat_metadata = {};
}
async function deleteLastMessage() {
@@ -1530,7 +1534,7 @@ function substituteParams(content, _name1, _name2, _original) {
_name1 = _name1 ?? name1;
_name2 = _name2 ?? name2;
if (!content) {
return ''
return '';
}
// Replace {{original}} with the original message
@@ -1548,6 +1552,11 @@ function substituteParams(content, _name1, _name2, _original) {
content = content.replace(/{{time}}/gi, moment().format('LT'));
content = content.replace(/{{date}}/gi, moment().format('LL'));
content = content.replace(/{{idle_duration}}/gi, () => getTimeSinceLastMessage());
content = content.replace(/{{time_UTC([-+]\d+)}}/gi, (_, offset) => {
const utcOffset = parseInt(offset, 10);
const utcTime = moment().utc().utcOffset(utcOffset).format('LT');
return utcTime;
});
content = randomReplace(content);
return content;
}
@@ -1909,8 +1918,9 @@ class StreamingProcessor {
onProgressStreaming(messageId, text, isFinal) {
const isImpersonate = this.type == "impersonate";
const isContinue = this.type == "continue";
text = this.removePrefix(text);
let processedText = cleanUpMessage(text, isImpersonate, !isFinal);
let processedText = cleanUpMessage(text, isImpersonate, isContinue, !isFinal);
let result = extractNameFromMessage(processedText, this.force_name2, isImpersonate);
let isName = result.this_mes_is_name;
processedText = result.getMessage;
@@ -2089,6 +2099,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// OpenAI doesn't need instruct mode. Use OAI main prompt instead.
const isInstruct = power_user.instruct.enabled && main_api !== 'openai';
const isImpersonate = type == "impersonate";
const isContinue = type == 'continue';
message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
// Name for the multigen prefix
@@ -2265,6 +2276,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
//////////////////////////////////
let chat2 = [];
let continue_mag = '';
for (let i = coreChat.length - 1, j = 0; i >= 0; i--, j++) {
// For OpenAI it's only used in WI
if (main_api == 'openai' && (!world_info || world_info.length === 0)) {
@@ -2275,8 +2287,9 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
chat2[i] = formatMessageHistoryItem(coreChat[j], isInstruct);
// Do not suffix the message for continuation
if (i === 0 && type == 'continue') {
if (i === 0 && isContinue) {
chat2[i] = chat2[i].slice(0, chat2[i].lastIndexOf(coreChat[j].mes) + coreChat[j].mes.length);
continue_mag = coreChat[j].mes;
}
}
@@ -2355,7 +2368,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
}
let cyclePrompt = '';
if (type == 'continue') {
if (isContinue) {
cyclePrompt = chat2.shift();
}
@@ -2404,15 +2417,16 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
console.debug('calling runGenerate');
streamingProcessor = isStreamingEnabled() ? new StreamingProcessor(type, force_name2) : false;
if (type == 'continue') {
if (isContinue) {
// Coping mechanism for OAI spacing
if ((main_api === 'openai' || main_api === 'poe') && !cyclePrompt.endsWith(' ')) {
cyclePrompt += ' ';
continue_mag += ' ';
}
// Save reply does add cycle text to the prompt, so it's not needed here
streamingProcessor && (streamingProcessor.firstMessageText = '');
message_already_generated = cyclePrompt;
message_already_generated = continue_mag;
tokens_already_generated = 1; // Multigen copium
}
@@ -2539,7 +2553,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
console.debug(`A prompt bias was found: ${promptBias}`);
mesSendString += `${name2}: ${promptBias}`;
}
} else if (power_user.user_prompt_bias) {
} else if (power_user.user_prompt_bias && !isImpersonate && !isInstruct) {
console.debug(`A prompt bias was found without character's name appended: ${promptBias}`);
mesSendString += substituteParams(power_user.user_prompt_bias);
}
@@ -2765,8 +2779,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
hideSwipeButtons();
let getMessage = await streamingProcessor.generate();
if (type == 'continue') {
getMessage = message_already_generated + getMessage;
if (isContinue) {
getMessage = continue_mag + getMessage;
}
if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) {
@@ -2804,7 +2818,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
({ type, getMessage } = saveReply('append', getMessage, this_mes_is_name, title));
}
} else {
let chunk = cleanUpMessage(message_already_generated, true, true);
let chunk = cleanUpMessage(message_already_generated, true, isContinue, true);
let extract = extractNameFromMessage(chunk, force_name2, isImpersonate);
$('#send_textarea').val(extract.getMessage).trigger('input');
}
@@ -2828,12 +2842,12 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
getMessage = message_already_generated.substring(substringStart);
}
if (type == 'continue') {
getMessage = message_already_generated + getMessage;
if (isContinue) {
getMessage = continue_mag + getMessage;
}
//Formating
getMessage = cleanUpMessage(getMessage, isImpersonate);
getMessage = cleanUpMessage(getMessage, isImpersonate, isContinue);
let this_mes_is_name;
({ this_mes_is_name, getMessage } = extractNameFromMessage(getMessage, force_name2, isImpersonate));
@@ -2945,6 +2959,10 @@ function getNextMessageId(type) {
}
export function getBiasStrings(textareaText, type) {
if (type == 'impersonate') {
return { messageBias: '', promptBias: '', isUserPromptBias: false };
}
let promptBias = '';
let messageBias = extractMessageBias(textareaText);
@@ -3217,7 +3235,8 @@ function promptItemize(itemizedPrompts, requestedMesId) {
//charDescriptionTokens +
//charPersonalityTokens +
//allAnchorsTokens +
//worldInfoStringTokens +
worldInfoStringTokens +
afterScenarioAnchorTokens +
examplesStringTokens;
// OAI doesn't use padding
thisPrompt_padding = 0;
@@ -3248,7 +3267,7 @@ function promptItemize(itemizedPrompts, requestedMesId) {
if (this_main_api == 'openai') {
//console.log('-- applying % on OAI tokens');
var oaiStartTokensPercentage = ((oaiStartTokens / (finalPromptTokens)) * 100).toFixed(2);
var storyStringTokensPercentage = ((oaiPromptTokens / (finalPromptTokens)) * 100).toFixed(2);
var storyStringTokensPercentage = (((examplesStringTokens + afterScenarioAnchorTokens + oaiPromptTokens) / (finalPromptTokens)) * 100).toFixed(2);
var ActualChatHistoryTokensPercentage = ((ActualChatHistoryTokens / (finalPromptTokens)) * 100).toFixed(2);
var promptBiasTokensPercentage = ((oaiBiasTokens / (finalPromptTokens)) * 100).toFixed(2);
var worldInfoStringTokensPercentage = ((worldInfoStringTokens / (finalPromptTokens)) * 100).toFixed(2);
@@ -3605,11 +3624,12 @@ function extractMessageFromData(data) {
}
}
function cleanUpMessage(getMessage, isImpersonate, displayIncompleteSentences = false) {
function cleanUpMessage(getMessage, isImpersonate, isContinue, displayIncompleteSentences = false) {
// Add the prompt bias before anything else
if (
power_user.user_prompt_bias &&
!isImpersonate &&
!isContinue &&
power_user.user_prompt_bias.length !== 0
) {
getMessage = substituteParams(power_user.user_prompt_bias) + getMessage;
@@ -5170,7 +5190,10 @@ function messageEditAuto(div) {
}
async function messageEditDone(div) {
const { mesBlock, text, mes, bias } = updateMessage(div);
let { mesBlock, text, mes, bias } = updateMessage(div);
if (this_edit_mes_id == 0) {
text = substituteParams(text);
}
mesBlock.find(".mes_text").empty();
mesBlock.find(".mes_edit_buttons").css("display", "none");
@@ -5681,11 +5704,7 @@ function read_bg_load(input) {
"background-image",
`url("${e.target.result}")`
);
$("#form_bg_download").after(
`<div class="bg_example" bgfile="${html}" style="background-image: url('${getThumbnailUrl('bg', html)}');">
<div class="bg_example_cross fa-solid fa-circle-xmark"></div>
</div>`
);
$("#form_bg_download").after(getBackgroundFromTemplate(html));
},
error: function (jqXHR, exception) {
console.log(exception);
@@ -6813,6 +6832,18 @@ async function doImpersonate() {
$("#option_impersonate").trigger('click', { fromSlashCommand: true })
}
async function doDeleteChat() {
$("#option_select_chat").trigger('click', { fromSlashCommand: true })
await delay(100)
let currentChatDeleteButton = $(".select_chat_block[highlight='true']").parent().find('.PastChat_cross')
$(currentChatDeleteButton).trigger('click', { fromSlashCommand: true })
await delay(1)
$("#dialogue_popup_ok").trigger('click')
//200 delay needed let the past chat view reshow first
await delay(200)
$("#select_chat_cross").trigger('click')
}
const isPwaMode = window.navigator.standalone;
if (isPwaMode) { $("body").addClass('PWA') }
@@ -6836,6 +6867,7 @@ $(document).ready(function () {
registerSlashCommand('dupe', DupeChar, [], " duplicates the currently selected character", true, true);
registerSlashCommand('api', connectAPISlash, [], "(kobold, horde, novel, ooba, oai, claude, poe, windowai) connect to an API", true, true);
registerSlashCommand('impersonate', doImpersonate, ['imp'], "- calls an impersonation response", true, true);
registerSlashCommand('delchat', doDeleteChat, [], "- deletes the current chat", true, true);
setTimeout(function () {
@@ -7069,6 +7101,41 @@ $(document).ready(function () {
});
});
$(document).on('click', '.bg_example_edit', async function (e) {
e.stopPropagation();
const old_bg = $(this).attr('bgfile');
if (!old_bg) {
console.debug('no bgfile');
return;
}
const fileExtension = old_bg.split('.').pop();
const old_bg_extensionless = old_bg.replace(`.${fileExtension}`, '');
const new_bg_extensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', old_bg_extensionless);
const new_bg = `${new_bg_extensionless}.${fileExtension}`;
if (old_bg_extensionless === new_bg_extensionless) {
console.debug('new_bg === old_bg');
return;
}
const data = { old_bg, new_bg };
const response = await fetch('/renamebackground', {
method: 'POST',
headers:getRequestHeaders(),
body: JSON.stringify(data),
cache: 'no-cache',
});
if (response.ok) {
await getBackgrounds();
} else {
toastr.warning('Failed to rename background');
}
});
$(document).on("click", ".bg_example_cross", function (e) {
e.stopPropagation();
bg_file_for_del = $(this);
@@ -7080,7 +7147,7 @@ $(document).ready(function () {
$(document).on("click", ".PastChat_cross", function () {
chat_file_for_del = $(this).attr('file_name');
console.log('detected cross click for' + chat_file_for_del);
console.debug('detected cross click for' + chat_file_for_del);
popup_type = "del_chat";
callPopup("<h3>Delete the Chat File?</h3>");
});
@@ -7231,7 +7298,6 @@ $(document).ready(function () {
chat_metadata = {};
characters[this_chid].chat = name2 + " - " + humanizedDateTime();
$("#selected_chat_pole").val(characters[this_chid].chat);
saveCharacterDebounced();
getChat();
}
}
@@ -7540,15 +7606,20 @@ $(document).ready(function () {
var id = $(this).attr("id");
if (id == "option_select_chat") {
if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press)) {
if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) {
displayPastChats();
$("#shadow_select_chat_popup").css("display", "block");
$("#shadow_select_chat_popup").css("opacity", 0.0);
$("#shadow_select_chat_popup").transition({
opacity: 1.0,
duration: animation_duration,
easing: animation_easing,
});
//this is just to avoid the shadow for past chat view when using /delchat
//however, the dialog popup still gets one..
if (!fromSlashCommand) {
console.log('displaying shadow')
$("#shadow_select_chat_popup").css("display", "block");
$("#shadow_select_chat_popup").css("opacity", 0.0);
$("#shadow_select_chat_popup").transition({
opacity: 1.0,
duration: animation_duration,
easing: animation_easing,
});
}
}
}
@@ -8333,7 +8404,7 @@ $(document).ready(function () {
var icon = $(this).find('.inline-drawer-icon');
icon.toggleClass('down up');
icon.toggleClass('fa-circle-chevron-down fa-circle-chevron-up');
$(this).closest('.inline-drawer').find('.inline-drawer-content').slideToggle();
$(this).closest('.inline-drawer').find('.inline-drawer-content').stop().slideToggle();
});
$(document).on('click', '.mes .avatar', function () {

View File

@@ -5,15 +5,14 @@ import {
getTokenCount,
saveSettingsDebounced,
this_chid,
} from "../../../script.js";
import { selected_group } from "../../group-chats.js";
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
import { registerSlashCommand } from "../../slash-commands.js";
import { getCharaFilename, debounce, waitUntilCondition, delay } from "../../utils.js";
} from "../script.js";
import { selected_group } from "./group-chats.js";
import { extension_settings, getContext, saveMetadataDebounced } from "./extensions.js";
import { registerSlashCommand } from "./slash-commands.js";
import { getCharaFilename, debounce, waitUntilCondition, delay } from "./utils.js";
export { MODULE_NAME as NOTE_MODULE_NAME };
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
const UPDATE_INTERVAL = 1000;
const DEFAULT_DEPTH = 4;
const DEFAULT_POSITION = 1;
@@ -352,110 +351,10 @@ function onChatChanged() {
$('#extension_floating_default_token_counter').text(tokenCounter3);
}
function addExtensionsSettings() {
const settingsHtml = `
<div id="floatingPrompt" class="drawer-content flexGap5">
<div class="panelControlBar flex-container">
<div id="floatingPromptheader" class="fa-solid fa-grip drag-grabber"></div>
<div id="ANClose" class="fa-solid fa-circle-xmark"></div>
</div>
<div name="floatingPromptHolder">
<div class="inline-drawer">
<div id="ANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>
<b>Unique to this chat</b>.<br>
Bookmarks inherit the Note from their parent, and can be changed individually after that.<br>
</small>
<textarea id="extension_floating_prompt" class="text_pole" rows="8" maxlength="10000"></textarea>
<div class="extension_token_counter">Tokens: <span id="extension_floating_prompt_token_counter">0</small></div>
<div class="floating_prompt_radio_group">
<label>
<input type="radio" name="extension_floating_position" value="0" />
After scenario
</label>
<label>
<input type="radio" name="extension_floating_position" value="1" />
In-chat @ Depth <input id="extension_floating_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
</label>
</div>
<!--<label for="extension_floating_interval">In-Chat Insertion Depth</label>-->
<label for="extension_floating_interval">Insertion Frequency</label>
<input id="extension_floating_interval" class="text_pole widthUnset" type="number" min="0" max="999" /><small> (0 = Disable, 1 = Always)</small>
<br>
<span>User inputs until next insertion: <span id="extension_floating_counter">(disabled)</span></span>
</div>
</div>
<hr class="sysHR">
<div class="inline-drawer">
<div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Character Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>Will be automatically added as the author's note for this character. Will be used in groups, but can't be modified when a group chat is open.</small>
<textarea id="extension_floating_chara" class="text_pole" rows="8" maxlength="10000"
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
<div class="extension_token_counter">Tokens: <span id="extension_floating_chara_token_counter">0</small></div>
<label class="checkbox_label" for="extension_use_floating_chara">
<input id="extension_use_floating_chara" type="checkbox" />
<span data-i18n="Use character author's note">Use character author's note</span>
</label>
<div class="floating_prompt_radio_group">
<label>
<input type="radio" name="extension_floating_char_position" value="0" />
Replace Author's Note
</label>
<label>
<input type="radio" name="extension_floating_char_position" value="1" />
Top of Author's Note
</label>
<label>
<input type="radio" name="extension_floating_char_position" value="2" />
Bottom of Author's Note
</label>
</div>
</div>
</div>
<hr class="sysHR">
<div class="inline-drawer">
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Default Author's Note</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">
<small>Will be automatically added as the Author's Note for all new chats.</small>
<textarea id="extension_floating_default" class="text_pole" rows="8" maxlength="10000"
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
<div class="extension_token_counter">Tokens: <span id="extension_floating_default_token_counter">0</small></div>
</div>
</div>
</div>
</div>
`;
const ANButtonHtml = `
<a id="option_toggle_AN">
<i class="fa-lg fa-solid fa-note-sticky"></i>
<span data-i18n="Author's Note">Author's Note</span>
</a>
`;
$('#options .options-content').prepend(ANButtonHtml);
$('#movingDivs').append(settingsHtml);
// Inject extension when extensions_activating is fired
// Inserts the extension first since it's statically imported
jQuery(async () => {
await waitUntilCondition(() => eventSource !== undefined);
$('#extension_floating_prompt').on('input', onExtensionFloatingPromptInput);
$('#extension_floating_interval').on('input', onExtensionFloatingIntervalInput);
$('#extension_floating_depth').on('input', onExtensionFloatingDepthInput);
@@ -473,13 +372,6 @@ function addExtensionsSettings() {
setTimeout(function () { $('#floatingPrompt').hide() }, 200);
})
$("#option_toggle_AN").on('click', onANMenuItemClick);
}
// Inject extension when extensions_activating is fired
// Inserts the extension first since it's statically imported
jQuery(async () => {
await waitUntilCondition(() => eventSource !== undefined);
eventSource.on(event_types.EXTENSIONS_FIRST_LOAD, addExtensionsSettings);
registerSlashCommand('note', setNoteTextCommand, [], "<span class='monospace'>(text)</span> sets an author's note for the currently selected chat", true, true);
registerSlashCommand('depth', setNoteDepthCommand, [], "<span class='monospace'>(number)</span> sets an author's note depth for in-chat positioning", true, true);

View File

@@ -411,9 +411,9 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
let toggleElement = isActive || isDisabled ?
`<input type="checkbox" title="Click to toggle" data-name="${name}" class="${isActive ? 'toggle_disable' : 'toggle_enable'} ${checkboxClass}" ${isActive ? 'checked' : ''}>` :
`<input type="checkbox" title="Cannot enable extension" data-name="${name}" class="extension_missing ${checkboxClass}" disabled>`;
let deleteButton = isExternal ? `<span class="delete-button"><button class="btn_delete menu_button" data-name="${name.replace('third-party', '')}" title="Delete"><i class="fa-solid fa-trash-can"></i></button></span>` : '';
// if external, wrap the name in a link to the repo
let extensionHtml = `<hr>
@@ -423,7 +423,7 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
${originHtml}
<span class="${isActive ? "extension_enabled" : isDisabled ? "extension_disabled" : "extension_missing"}">
${DOMPurify.sanitize(displayName)}${displayVersion}
</span>
</span>
${isExternal ? '</a>' : ''}
<span style="float:right;">${toggleElement}</span>

View File

@@ -1,12 +0,0 @@
{
"display_name": "Author's Note",
"loading_order": 1,
"requires": [],
"optional": [],
"generate_interceptor": "AuthorNote_generateInterceptor",
"js": "index.js",
"css": "style.css",
"author": "Cohee#1207",
"version": "1.0.0",
"homePage": "https://github.com/SillyTavern/SillyTavern"
}

View File

@@ -1,60 +0,0 @@
#floatingPrompt {
overflow-y: auto;
max-width: 90svw;
max-height: 90svh;
min-width: 100px;
min-height: 100px;
border-radius: 10px;
border: 1px solid var(--white30a);
position: fixed;
padding: 10px;
padding-top: 25px;
display: none;
flex-direction: column;
box-shadow: 0 0 10px var(--black70a);
z-index: 3000;
left: 0;
top: 0;
margin: 0;
right: unset;
width: calc(((100svw - var(--sheldWidth)) / 2) - 1px);
}
.floating_prompt_radio_group {
display: flex;
flex-direction: column;
}
#extension_floating_counter {
font-weight: 600;
color: orange;
}
.extension_token_counter {
font-size: calc(var(--mainFontSize) * 0.9);
width: 100%;
text-align: right;
}
.floating_prompt_settings textarea {
font-size: calc(var(--mainFontSize) * 0.9);
line-height: 1.2;
}
#ANClose {
height: 15px;
aspect-ratio: 1 / 1;
font-size: 20px;
opacity: 0.5;
transition: all 250ms;
}
#ANClose:hover {
cursor: pointer;
opacity: 1;
}
#floatingPrompt .drag-grabber {
position: unset;
}

View File

@@ -128,19 +128,46 @@ function substituteRegexParams(rawString, regexMatch, { characterOverride, repla
finalString = substituteParams(finalString, undefined, characterOverride);
let overlaidMatch = regexMatch;
// TODO: Maybe move the for loops into a separate function?
if (replaceStrategy === regex_replace_strategy.OVERLAY) {
const splitReplace = finalString.split("{{match}}");
// There's a prefix
if (splitReplace[0]) {
// Fetch the prefix
const splicedPrefix = spliceSymbols(splitReplace[0], false);
overlaidMatch = overlaidMatch.replace(splicedPrefix, "").trim();
// Sequentially remove all occurrences of prefix from start of split
const splitMatch = overlaidMatch.split(splicedPrefix);
let sliceNum = 0;
for (let index = 0; index < splitMatch.length; index++) {
if (splitMatch[index].length === 0) {
sliceNum++;
} else {
break;
}
}
overlaidMatch = splitMatch.slice(sliceNum, splitMatch.length).join(splicedPrefix);
}
// There's a suffix
if (splitReplace[1]) {
// Fetch the suffix
const splicedSuffix = spliceSymbols(splitReplace[1], true);
overlaidMatch = overlaidMatch.replace(new RegExp(`${splicedSuffix}$`), "").trim();
// Sequential removal of all suffix occurrences from end of split
const splitMatch = overlaidMatch.split(splicedSuffix);
let sliceNum = 0;
for (let index = splitMatch.length - 1; index >= 0; index--) {
if (splitMatch[index].length === 0) {
sliceNum++;
} else {
break;
}
}
overlaidMatch = splitMatch.slice(0, splitMatch.length - sliceNum).join(splicedSuffix);
}
}
@@ -150,7 +177,7 @@ function substituteRegexParams(rawString, regexMatch, { characterOverride, repla
return finalString;
}
// Splices symbols and whitespace from the beginning and end of a string
// Splices common sentence symbols and whitespace from the beginning and end of a string
// Using a for loop due to sequential ordering
function spliceSymbols(rawString, isSuffix) {
let offset = 0;
@@ -163,5 +190,5 @@ function spliceSymbols(rawString, isSuffix) {
}
}
return isSuffix ? rawString.substring(0, rawString.length - offset) : rawString.substring(offset);;
return isSuffix ? rawString.substring(0, rawString.length - offset) : rawString.substring(offset);
}

View File

@@ -236,5 +236,25 @@ jQuery(async () => {
onRegexEditorOpenClick(false);
});
$('#saved_regex_scripts').sortable({
stop: function () {
let newScripts = [];
$('#saved_regex_scripts').children().each(function () {
const scriptName = $(this).find(".regex_script_name").text();
const existingScript = extension_settings.regex.find((e) => e.scriptName === scriptName);
if (existingScript) {
newScripts.push(existingScript);
}
});
extension_settings.regex = newScripts;
saveSettingsDebounced();
console.debug("Regex scripts reordered");
// TODO: Maybe reload regex scripts after move
},
});
await loadRegexScripts();
$("#saved_regex_scripts").sortable("enable");
});

View File

@@ -1,4 +1,5 @@
<div class="regex-script-label flex-container flexnowrap">
<span class="drag-handle menu-handle">&#9776;</span>
<div class="regex_script_name flexGrow overflow-hidden"></div>
<div class="flex-container flexnowrap">
<div class="edit_existing_regex menu_button">

View File

@@ -539,9 +539,9 @@ function getSystemPrompt(systemPrompt, nsfw_toggle_prompt, enhance_definitions_p
return whole_prompt;
}
function tryParseStreamingError(str) {
function tryParseStreamingError(response, decoded) {
try {
const data = JSON.parse(str);
const data = JSON.parse(decoded);
if (!data) {
return;
@@ -550,7 +550,7 @@ function tryParseStreamingError(str) {
checkQuotaError(data);
if (data.error) {
toastr.error(response.statusText, 'API returned an error');
toastr.error(data.error.message || response.statusText, 'API returned an error');
throw new Error(data);
}
}
@@ -784,26 +784,26 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
let messageBuffer = "";
while (true) {
const { done, value } = await reader.read();
let response = decoder.decode(value);
let decoded = decoder.decode(value);
// Claude's streaming SSE messages are separated by \r
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
response = response.replace(/\r/g, "");
decoded = decoded.replace(/\r/g, "");
}
tryParseStreamingError(response);
tryParseStreamingError(response, decoded);
let eventList = [];
// ReadableStream's buffer is not guaranteed to contain full SSE messages as they arrive in chunks
// We need to buffer chunks until we have one or more full messages (separated by double newlines)
if (!oai_settings.legacy_streaming) {
messageBuffer += response;
messageBuffer += decoded;
eventList = messageBuffer.split("\n\n");
// Last element will be an empty string or a leftover partial message
messageBuffer = eventList.pop();
} else {
eventList = response.split("\n");
eventList = decoded.split("\n");
}
for (let event of eventList) {
@@ -837,7 +837,7 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
checkQuotaError(data);
if (data.error) {
toastr.error(response.statusText, 'API returned an error');
toastr.error(data.error.message || response.statusText, 'API returned an error');
throw new Error(data);
}
@@ -999,30 +999,50 @@ export function getTokenizerModel() {
return oai_settings.openai_model;
}
const turboTokenizer = 'gpt-3.5-turbo';
const gpt4Tokenizer = 'gpt-4';
const gpt2Tokenizer = 'gpt2';
const claudeTokenizer = 'claude';
// Assuming no one would use it for different models.. right?
if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) {
return 'gpt-4';
return gpt4Tokenizer;
}
const turboTokenizer = 'gpt-3.5-turbo'
// Select correct tokenizer for WindowAI proxies
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) {
if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI && oai_settings.windowai_model) {
if (oai_settings.windowai_model.includes('gpt-4')) {
return 'gpt-4';
return gpt4Tokenizer;
}
else if (oai_settings.windowai_model.includes('gpt-3.5-turbo')) {
return turboTokenizer;
}
else if (oai_settings.windowai_model.includes('claude')) {
return 'claude';
return claudeTokenizer;
}
else if (oai_settings.windowai_model.includes('GPT-NeoXT')) {
return 'gpt2';
return gpt2Tokenizer;
}
}
// And for OpenRouter (if not a site model, then it's impossible to determine the tokenizer)
if (oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER && oai_settings.openrouter_model) {
if (oai_settings.openrouter_model.includes('gpt-4')) {
return gpt4Tokenizer;
}
else if (oai_settings.openrouter_model.includes('gpt-3.5-turbo')) {
return turboTokenizer;
}
else if (oai_settings.openrouter_model.includes('claude')) {
return claudeTokenizer;
}
else if (oai_settings.openrouter_model.includes('GPT-NeoXT')) {
return gpt2Tokenizer;
}
}
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
return 'claude';
return claudeTokenizer;
}
// Default to Turbo 3.5

View File

@@ -58,11 +58,6 @@ export const chat_styles = {
DOCUMENT: 2,
}
const sheld_width = {
DEFAULT: 0,
w1000px: 1,
}
const pygmalion_options = {
DISABLED: -1,
AUTO: 0,
@@ -117,7 +112,7 @@ let power_user = {
fast_ui_mode: true,
avatar_style: avatar_styles.ROUND,
chat_display: chat_styles.DEFAULT,
sheld_width: sheld_width.DEFAULT,
chat_width: 50,
never_resize_avatars: false,
show_card_avatar_urls: false,
play_message_sound: false,
@@ -143,9 +138,11 @@ let power_user = {
waifuMode: false,
movingUI: false,
movingUIState: {},
movingUIPreset: '',
noShadows: false,
theme: 'Default (Dark) 1.7.1',
auto_swipe: false,
auto_swipe_minimum_length: 0,
auto_swipe_blacklist: [],
@@ -193,13 +190,14 @@ let power_user = {
};
let themes = [];
let movingUIPresets = [];
let instruct_presets = [];
const storage_keys = {
fast_ui_mode: "TavernAI_fast_ui_mode",
avatar_style: "TavernAI_avatar_style",
chat_display: "TavernAI_chat_display",
sheld_width: "TavernAI_sheld_width",
chat_width: "chat_width",
font_scale: "TavernAI_font_scale",
main_text_color: "TavernAI_main_text_color",
@@ -406,18 +404,11 @@ function applyChatDisplay() {
}
}
function applySheldWidth() {
//disabled due to 50vw conversion
return
power_user.sheld_width = Number(localStorage.getItem(storage_keys.sheld_width) ?? chat_styles.DEFAULT);
$("body").toggleClass("w1000px", power_user.sheld_width === sheld_width.w1000px);
function applyChatWidth() {
power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
let r = document.documentElement;
if (power_user.sheld_width === 1) {
r.style.setProperty('--sheldWidth', '1000px');
} else {
r.style.setProperty('--sheldWidth', '800px');
}
$(`input[name="sheld_width"][value="${power_user.sheld_width}"]`).prop("checked", true);
r.style.setProperty('--sheldWidth', `${power_user.chat_width}vw`);
$('#chat_width_slider').val(power_user.chat_width);
}
async function applyThemeColor(type) {
@@ -542,10 +533,10 @@ async function applyTheme(name) {
}
},
{
key: 'sheld_width',
key: 'chat_width',
action: async () => {
localStorage.setItem(storage_keys.sheld_width, power_user.sheld_width);
applySheldWidth();
localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
applyChatWidth();
}
},
{
@@ -594,10 +585,25 @@ async function applyTheme(name) {
console.log('theme applied: ' + name);
}
async function applyMovingUIPreset(name) {
resetMovablePanels('quiet')
const movingUIPreset = movingUIPresets.find(x => x.name == name);
if (!movingUIPreset) {
return;
}
power_user.movingUIState = movingUIPreset.movingUIState;
console.log('MovingUI Preset applied: ' + name);
loadMovingUIState()
}
switchUiMode();
applyFontScale();
applyThemeColor();
applySheldWidth();
applyChatWidth();
applyAvatarStyle();
applyBlurStrength();
applyShadowWidth();
@@ -618,6 +624,10 @@ function loadPowerUserSettings(settings, data) {
themes = data.themes;
}
if (data.movingUIPresets !== undefined) {
movingUIPresets = data.movingUIPresets;
}
if (data.instruct !== undefined) {
instruct_presets = data.instruct;
}
@@ -639,7 +649,7 @@ function loadPowerUserSettings(settings, data) {
power_user.mesIDDisplay_enabled = mesIDDisplay === null ? true : mesIDDisplay == "true";
power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
//power_user.chat_display = Number(localStorage.getItem(storage_keys.chat_display) ?? chat_styles.DEFAULT);
power_user.sheld_width = Number(localStorage.getItem(storage_keys.sheld_width) ?? sheld_width.DEFAULT);
power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
power_user.blur_strength = Number(localStorage.getItem(storage_keys.blur_strength) ?? 10);
@@ -706,7 +716,7 @@ function loadPowerUserSettings(settings, data) {
$("#prefer_character_jailbreak").prop("checked", power_user.prefer_character_jailbreak);
$(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop("checked", true);
$(`#chat_display option[value=${power_user.chat_display}]`).attr("selected", true).trigger('change');
$(`input[name="sheld_width"][value="${power_user.sheld_width}"]`).prop("checked", true);
$('#chat_width_slider').val(power_user.chat_width);
$("#token_padding").val(power_user.token_padding);
$("#font_scale").val(power_user.font_scale);
@@ -735,6 +745,15 @@ function loadPowerUserSettings(settings, data) {
$("#themes").append(option);
}
for (const movingUIPreset of movingUIPresets) {
const option = document.createElement('option');
option.value = movingUIPreset.name;
option.innerText = movingUIPreset.name;
option.selected = movingUIPreset.name == power_user.movingUIPreset;
$("#movingUIPresets").append(option);
}
$(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true);
sortCharactersList();
reloadMarkdownProcessor(power_user.render_formulas);
@@ -917,6 +936,10 @@ export function formatInstructModePrompt(name, isImpersonate, promptBias, name1,
const sortFunc = (a, b) => power_user.sort_order == 'asc' ? compareFunc(a, b) : compareFunc(b, a);
const compareFunc = (first, second) => {
if (power_user.sort_order == 'random') {
return Math.random() > 0.5 ? 1 : -1;
}
switch (power_user.sort_rule) {
case 'boolean':
const a = first[power_user.sort_field];
@@ -999,7 +1022,7 @@ async function saveTheme() {
avatar_style: power_user.avatar_style,
chat_display: power_user.chat_display,
noShadows: power_user.noShadows,
sheld_width: power_user.sheld_width,
chat_width: power_user.chat_width,
timer_enabled: power_user.timer_enabled,
timestamps_enabled: power_user.timestamps_enabled,
mesIDDisplay_enabled: power_user.mesIDDisplay_enabled,
@@ -1034,6 +1057,48 @@ async function saveTheme() {
}
}
async function saveMovingUI() {
const name = await callPopup('Enter a name for the MovingUI Preset:', 'input');
if (!name) {
return;
}
const movingUIPreset = {
name,
movingUIState: power_user.movingUIState
}
console.log(movingUIPreset)
const response = await fetch('/savemovingui', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(movingUIPreset)
});
if (response.ok) {
const movingUIPresetIndex = movingUIPresets.findIndex(x => x.name == name);
if (movingUIPresetIndex == -1) {
movingUIPresets.push(movingUIPreset);
const option = document.createElement('option');
option.selected = true;
option.value = name;
option.innerText = name;
$('#movingUIPresets').append(option);
}
else {
movingUIPresets[movingUIPresetIndex] = movingUIPreset;
$(`#movingUIPresets option[value="${name}"]`).attr('selected', true);
}
power_user.movingUIPreset = name;
saveSettingsDebounced();
} else {
toastr.warning('failed to save MovingUI state.')
}
}
async function resetMovablePanels(type) {
const panelIds = [
'sheld',
@@ -1072,13 +1137,25 @@ async function resetMovablePanels(type) {
await delay(50)
power_user.movingUIState = {};
//if user manually resets panels, deselect the current preset
if (type !== 'quiet' && type !== 'resize') {
power_user.movingUIPreset = 'Default'
$(`#movingUIPresets option[value="Default"]`).prop('selected', true);
}
saveSettingsDebounced();
eventSource.emit(event_types.MOVABLE_PANELS_RESET);
eventSource.once(event_types.SETTINGS_UPDATED, () => {
$(".resizing").removeClass('resizing');
if (type === 'resize') {
//if happening as part of preset application, do it quietly.
if (type === 'quiet') {
return
//if happening due to resize, tell user.
} else if (type === 'resize') {
toastr.warning('Panel positions reset due to zoom/resize');
//if happening due to manual button press
} else {
toastr.success('Panel positions reset');
}
@@ -1578,11 +1655,10 @@ $(document).ready(() => {
});
$(`input[name="sheld_width"]`).on('input', function (e) {
power_user.sheld_width = Number(e.target.value);
localStorage.setItem(storage_keys.sheld_width, power_user.sheld_width);
//console.log("sheld width changing now");
applySheldWidth();
$('#chat_width_slider').on('input', function (e) {
power_user.chat_width = Number(e.target.value);
localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
applyChatWidth();
});
$(`input[name="font_scale"]`).on('input', async function (e) {
@@ -1655,7 +1731,17 @@ $(document).ready(() => {
saveSettingsDebounced();
});
$("#movingUIPresets").on('change', async function () {
console.log('saw MUI preset change')
const movingUIPresetSelected = $(this).find(':selected').val();
power_user.movingUIPreset = movingUIPresetSelected;
applyMovingUIPreset(movingUIPresetSelected);
saveSettingsDebounced();
});
$("#ui-preset-save-button").on('click', saveTheme);
$("#movingui-preset-save-button").on('click', saveMovingUI);
$("#never_resize_avatars").on('input', function () {
power_user.never_resize_avatars = !!$(this).prop('checked');

View File

@@ -20,6 +20,7 @@ import {
replaceCurrentChat,
setCharacterId,
generateQuietPrompt,
reloadCurrentChat,
} from "../script.js";
import { humanizedDateTime } from "./RossAscends-mods.js";
import { resetSelectedGroup } from "./group-chats.js";
@@ -125,11 +126,46 @@ parser.addCommand('flat', setFlatModeCallback, ['default'], ' sets the messa
parser.addCommand('continue', continueChatCallback, ['cont'], ' continues the last message in the chat', true, true);
parser.addCommand('go', goToCharacterCallback, ['char'], '<span class="monospace">(name)</span> opens up a chat with the character by its name', true, true);
parser.addCommand('sysgen', generateSystemMessage, [], '<span class="monospace">(prompt)</span> generates a system message using a specified prompt', true, true);
parser.addCommand('delname', deleteMessagesByNameCallback, ['cancel'], '<span class="monospace">(name)</span> deletes all messages attributed to a specified name', true, true);
const NARRATOR_NAME_KEY = 'narrator_name';
const NARRATOR_NAME_DEFAULT = 'System';
const COMMENT_NAME_DEFAULT = 'Note';
async function deleteMessagesByNameCallback(_, name) {
if (!name) {
console.warn('WARN: No name provided for /delname command');
return;
}
name = name.trim();
const messagesToDelete = [];
chat.forEach((value) => {
if (value.name === name) {
messagesToDelete.push(value);
}
});
if (!messagesToDelete.length) {
console.debug('/delname: Nothing to delete');
return;
}
for (const message of messagesToDelete) {
const index = chat.indexOf(message);
if (index !== -1) {
console.debug(`/delname: Deleting message #${index}`, message);
chat.splice(index, 1);
}
}
await saveChatConditional();
await reloadCurrentChat();
toastr.info(`Deleted ${messagesToDelete.length} messages from ${name}`);
}
function findCharacterIndex(name) {
const matchTypes = [
(a, b) => a === b,

View File

@@ -311,7 +311,7 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
}
if (tag.excluded) {
isGeneralList ? $(tagElement).addClass('excluded') : $(listElement).parent().parent().addClass('hiddenByTag');
isGeneralList ? $(tagElement).addClass('excluded') : $(listElement).closest('.character_select, .group_select').addClass('hiddenByTag');
}
if (selectable) {
@@ -329,7 +329,7 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
$(listElement).append(tagElement);
}
function onTagFilterClick(listElement, characterSelector) {
function onTagFilterClick(listElement, characterSelector) {
let excludeTag;
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');

View File

@@ -38,6 +38,9 @@ let textgenerationwebui_settings = {
skip_special_tokens: true,
streaming: false,
streaming_url: 'ws://127.0.0.1:5005/api/v1/stream',
mirostat_mode: 0,
mirostat_tau: 5,
mirostat_eta: 0.1,
};
let textgenerationwebui_presets = [];
@@ -67,6 +70,9 @@ const setting_names = [
"skip_special_tokens",
"streaming",
"streaming_url",
"mirostat_mode",
"mirostat_tau",
"mirostat_eta",
];
function selectPreset(name) {
@@ -224,5 +230,8 @@ export function getTextGenGenerationData(finalPromt, this_amount_gen, isImperson
'tfs': textgenerationwebui_settings.tfs,
'epsilon_cutoff': textgenerationwebui_settings.epsilon_cutoff,
'eta_cutoff': textgenerationwebui_settings.eta_cutoff,
'mirostat_mode': textgenerationwebui_settings.mirostat_mode,
'mirostat_tau': textgenerationwebui_settings.mirostat_tau,
'mirostat_eta': textgenerationwebui_settings.mirostat_eta,
};
}

View File

@@ -86,6 +86,10 @@ export async function parseJsonFile(file) {
}
export function getStringHash(str, seed = 0) {
if (typeof str !== 'string') {
return 0;
}
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {

View File

@@ -1,7 +1,7 @@
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, delay, getCharaFilename, deepClone } from "./utils.js";
import { getContext } from "./extensions.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
import { registerSlashCommand } from "./slash-commands.js";
import { deviceInfo } from "./RossAscends-mods.js";
@@ -961,9 +961,13 @@ async function checkWorldInfo(chat, maxContext) {
console.debug(`uid:${entry.uid}: checking logic: ${entry.selectiveLogic}`)
secondary: for (let keysecondary of entry.keysecondary) {
const secondarySubstituted = substituteParams(keysecondary);
console.debug(`uid:${entry.uid}: filtering ${secondarySubstituted}`)
console.debug(`uid:${entry.uid}: filtering ${secondarySubstituted}`);
// If selectiveLogic isn't found, assume it's AND
const selectiveLogic = entry.selectiveLogic ?? 0;
//AND operator
if (entry.selectiveLogic === 0) {
if (selectiveLogic === 0) {
console.debug('saw AND logic, checking..')
if (secondarySubstituted && matchKeys(textToScan, secondarySubstituted.trim())) {
console.log(`activating entry ${entry.uid} with AND found`)
@@ -972,7 +976,7 @@ async function checkWorldInfo(chat, maxContext) {
}
}
//NOT operator
if (entry.selectiveLogic === 1) {
if (selectiveLogic === 1) {
console.debug(`uid ${entry.uid}: checking NOT logic for ${secondarySubstituted}`)
if (secondarySubstituted && matchKeys(textToScan, secondarySubstituted.trim())) {
console.debug(`uid ${entry.uid}: canceled; filtered out by ${secondarySubstituted}`)

View File

@@ -548,7 +548,7 @@ hr {
#options,
#extensionsMenu {
display: flex;
z-index: 100;
z-index: 29999;
background-color: var(--SmartThemeBlurTintColor);
-webkit-backdrop-filter: blur(var(--SmartThemeBlurStrength));
backdrop-filter: blur(var(--SmartThemeBlurStrength));
@@ -695,6 +695,7 @@ hr {
display: flex;
flex-direction: column;
gap: 5px;
flex-grow: 1;
}
#avatars-style .range-block-range,
@@ -1205,6 +1206,7 @@ body.charListGrid #rm_print_characters_block {
flex-wrap: wrap;
flex-direction: row;
justify-content: space-around;
align-content: flex-start;
}
body.charListGrid #rm_print_characters_block .character_select {
@@ -1264,6 +1266,67 @@ body.charListGrid #rm_print_characters_block .tags_inline {
overflow-y: auto;
}
#floatingPrompt {
overflow-y: auto;
max-width: 90svw;
max-height: 90svh;
min-width: 100px;
min-height: 100px;
border-radius: 10px;
border: 1px solid var(--white30a);
position: fixed;
padding: 10px;
padding-top: 25px;
display: none;
flex-direction: column;
box-shadow: 0 0 10px var(--black70a);
z-index: 3000;
left: 0;
top: 0;
margin: 0;
right: unset;
width: calc(((100svw - var(--sheldWidth)) / 2) - 1px);
}
.floating_prompt_radio_group {
display: flex;
flex-direction: column;
}
#extension_floating_counter {
font-weight: 600;
color: orange;
}
.extension_token_counter {
font-size: calc(var(--mainFontSize) * 0.9);
width: 100%;
text-align: right;
}
.floating_prompt_settings textarea {
font-size: calc(var(--mainFontSize) * 0.9);
line-height: 1.2;
}
#ANClose {
height: 15px;
aspect-ratio: 1 / 1;
font-size: 20px;
opacity: 0.5;
transition: all 250ms;
}
#ANClose:hover {
cursor: pointer;
opacity: 1;
}
#floatingPrompt .drag-grabber {
position: unset;
}
/* ################################################################*/
/* CUSTOMIZE THE DROPDOWN SELECT COLORS FOR RIGHT MENU
/*#################################################################*/
@@ -1563,6 +1626,7 @@ body.big-avatars .ch_description {
cursor: pointer;
aspect-ratio: 16/9;
justify-content: flex-end;
position: relative;
}
.BGSampleTitle {
@@ -1579,12 +1643,10 @@ body.big-avatars .ch_description {
font-size: calc(var(--fontScale) * 0.9em);
}
.bg_example_cross {
.bg_button {
width: 15px;
height: 15px;
position: relative;
/* float: right; */
right: 10px;
position: absolute;
top: 5px;
cursor: pointer;
opacity: 0.7;
@@ -1595,6 +1657,19 @@ body.big-avatars .ch_description {
padding: 0;
margin: 0;
filter: drop-shadow(0px 0px 3px white);
transition: opacity 0.2s ease-in-out;
}
.bg_button:hover {
opacity: 1;
}
.bg_example_cross {
right: 10px;
}
.bg_example_edit {
left: 10px;
}
.no-border {
@@ -2660,6 +2735,7 @@ input[type="range"]::-webkit-slider-thumb {
display: flex;
flex-direction: column;
row-gap: 5px;
flex-grow: 1;
}
#anchor_checkbox label,
@@ -3594,12 +3670,13 @@ span.warning {
font-weight: bolder;
}
#talkativeness_hint {
.slider_hint {
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: space-between;
width: 100%;
position: relative;
}
#talkativeness_expander {
@@ -3611,21 +3688,14 @@ span.warning {
width: 100%;
}
#talkativeness_hint span {
min-width: 10em;
.slider_hint span {
font-size: calc(var(--mainFontSize) - .25rem);
}
#talkativeness_hint span:nth-child(1) {
text-align: left;
}
#talkativeness_hint span:nth-child(2) {
text-align: center;
}
#talkativeness_hint span:nth-child(3) {
text-align: right;
.slider_hint span:nth-child(2) {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
p {
@@ -4062,6 +4132,7 @@ input.extension_missing[type="checkbox"] {
justify-content: space-between;
align-items: center;
padding: 5px 0;
cursor: pointer;
}
.inline-drawer-content {
@@ -4096,7 +4167,7 @@ input.extension_missing[type="checkbox"] {
margin: 0 auto;
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
z-index: 9999 !important;
z-index: 1000 !important;
}
/*to prevent draggables from being made too small to see*/
@@ -4702,7 +4773,7 @@ body.waifuMode .zoomed_avatar {
}
.zoomed_avatar img {
border: 1px solid var(--white50a);
/* border: 1px solid var(--white50a); */
border-radius: 20px;
height: 100%;
width: 100%;
@@ -4856,6 +4927,7 @@ body.waifuMode .zoomed_avatar {
align-items: start;
height: min-content;
align-content: start;
max-width: unset;
}
#top-settings-holder,
@@ -4991,10 +5063,6 @@ body.waifuMode .zoomed_avatar {
max-height: 190px;
}
#talkativeness_hint span {
min-width: 33%;
}
.drawer25pWidth {
flex-basis: max(calc(100% / 4 - 10px), 190px);
}

145
server.js
View File

@@ -262,6 +262,7 @@ const directories = {
thumbnailsBg: 'thumbnails/bg/',
thumbnailsAvatar: 'thumbnails/avatar/',
themes: 'public/themes',
movingUI: 'public/movingUI',
extensions: 'public/scripts/extensions',
instruct: 'public/instruct',
context: 'public/context',
@@ -395,7 +396,7 @@ app.post("/generate", jsonParser, async function (request, response_generate = r
const controller = new AbortController();
request.socket.removeAllListeners('close');
request.socket.on('close', async function () {
if (request.body.can_abort && !response_generate.finished) {
if (request.body.can_abort && !response_generate.writableEnded) {
try {
console.log('Aborting Kobold generation...');
// send abort signal to koboldcpp
@@ -765,7 +766,7 @@ function convertToV2(char) {
tags: char.tags,
});
result.chat = char.chat;
result.chat = char.chat ?? humanizedISO8601DateTime();
result.create_date = char.create_date;
return result;
}
@@ -825,6 +826,8 @@ function readFromV2(char) {
char[charField] = v2Value;
});
char['chat'] = char['chat'] ?? humanizedISO8601DateTime();
return char;
}
@@ -1075,7 +1078,7 @@ app.post("/editcharacterattribute", jsonParser, async function (request, respons
charaWrite(avatarPath, char, (request.body.avatar_url).replace('.png', ''), response, 'Character saved');
}).catch((err) => {
console.error('An error occured, character edit invalidated.', err);
} );
});
}
catch {
console.error('An error occured, character edit invalidated.');
@@ -1404,40 +1407,44 @@ app.post("/delchat", jsonParser, function (request, response) {
return response.send('ok');
});
app.post('/renamebackground', jsonParser, function (request, response) {
if (!request.body) return response.sendStatus(400);
const oldFileName = path.join('public/backgrounds/', sanitize(request.body.old_bg));
const newFileName = path.join('public/backgrounds/', sanitize(request.body.new_bg));
if (!fs.existsSync(oldFileName)) {
console.log('BG file not found');
return response.sendStatus(400);
}
if (fs.existsSync(newFileName)) {
console.log('New BG file already exists');
return response.sendStatus(400);
}
fs.renameSync(oldFileName, newFileName);
invalidateThumbnail('bg', request.body.old_bg);
return response.send('ok');
});
app.post("/downloadbackground", urlencodedParser, function (request, response) {
response_dw_bg = response;
if (!request.body) return response.sendStatus(400);
let filedata = request.file;
//console.log(filedata.mimetype);
var fileType = ".png";
var img_file = "ai";
var img_path = "public/img/";
img_path = "uploads/";
img_file = filedata.filename;
if (filedata.mimetype == "image/jpeg") fileType = ".jpeg";
if (filedata.mimetype == "image/png") fileType = ".png";
if (filedata.mimetype == "image/gif") fileType = ".gif";
if (filedata.mimetype == "image/bmp") fileType = ".bmp";
fs.copyFile(img_path + img_file, 'public/backgrounds/' + img_file + fileType, (err) => {
invalidateThumbnail('bg', img_file + fileType);
if (err) {
return console.log(err);
} else {
//console.log(img_file+fileType);
response_dw_bg.send(img_file + fileType);
}
//console.log('The image was copied from temp directory.');
});
if (!request.body || !request.file) return response.sendStatus(400);
const img_path = path.join("uploads/", request.file.filename);
const filename = request.file.originalname;
try {
fs.copyFileSync(img_path, path.join('public/backgrounds/', filename));
invalidateThumbnail('bg', filename);
response_dw_bg.send(filename);
} catch (err) {
console.error(err);
response_dw_bg.sendStatus(500);
}
});
app.post("/savesettings", jsonParser, function (request, response) {
fs.writeFile('public/settings.json', JSON.stringify(request.body, null, 4), 'utf8', function (err) {
if (err) {
@@ -1494,6 +1501,10 @@ function sortByModifiedDate(directory) {
return (a, b) => new Date(fs.statSync(`${directory}/${b}`).mtime) - new Date(fs.statSync(`${directory}/${a}`).mtime);
}
function sortByName(_) {
return (a, b) => a.localeCompare(b);
}
function readPresetsFromDirectory(directoryPath, options = {}) {
const {
sortFunction,
@@ -1530,7 +1541,7 @@ app.post('/getsettings', jsonParser, (request, response) => {
// NovelAI Settings
const { fileContents: novelai_settings, fileNames: novelai_setting_names }
= readPresetsFromDirectory(directories.novelAI_Settings, {
sortFunction: sortByModifiedDate(directories.novelAI_Settings),
sortFunction: sortByName(directories.novelAI_Settings),
removeFileExtension: true
});
@@ -1543,13 +1554,13 @@ app.post('/getsettings', jsonParser, (request, response) => {
// TextGenerationWebUI Settings
const { fileContents: textgenerationwebui_presets, fileNames: textgenerationwebui_preset_names }
= readPresetsFromDirectory(directories.textGen_Settings, {
sortFunction: sortByModifiedDate(directories.textGen_Settings), removeFileExtension: true
sortFunction: sortByName(directories.textGen_Settings), removeFileExtension: true
});
//Kobold
const { fileContents: koboldai_settings, fileNames: koboldai_setting_names }
= readPresetsFromDirectory(directories.koboldAI_Settings, {
sortFunction: sortByModifiedDate(directories.koboldAI_Settings), removeFileExtension: true
sortFunction: sortByName(directories.koboldAI_Settings), removeFileExtension: true
})
const worldFiles = fs
@@ -1559,6 +1570,7 @@ app.post('/getsettings', jsonParser, (request, response) => {
const world_names = worldFiles.map(item => path.parse(item).name);
const themes = readAndParseFromDirectory(directories.themes);
const movingUIPresets = readAndParseFromDirectory(directories.movingUI);
const instruct = readAndParseFromDirectory(directories.instruct);
const context = readAndParseFromDirectory(directories.context);
@@ -1574,6 +1586,7 @@ app.post('/getsettings', jsonParser, (request, response) => {
textgenerationwebui_presets,
textgenerationwebui_preset_names,
themes,
movingUIPresets,
instruct,
context,
enable_extensions: enableExtensions,
@@ -1619,6 +1632,17 @@ app.post('/savetheme', jsonParser, (request, response) => {
return response.sendStatus(200);
});
app.post('/savemovingui', jsonParser, (request, response) => {
if (!request.body || !request.body.name) {
return response.sendStatus(400);
}
const filename = path.join(directories.movingUI, sanitize(request.body.name) + '.json');
fs.writeFileSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
return response.sendStatus(200);
});
function convertWorldInfoToCharacterBook(name, entries) {
const result = { entries: [], name };
@@ -3469,9 +3493,26 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
function handleError(error, response_generate_openai, request) {
console.error('Error:', error.message);
let message = error?.response?.statusText;
switch (error?.response?.status) {
case 402:
message = 'Credit limit reached';
console.log(message);
break;
case 403:
message = 'API key disabled or exhausted';
console.log(message);
break;
}
const quota_error = error?.response?.status === 429 && error?.response?.data?.error?.type === 'insufficient_quota';
const response = { error: { message }, quota_error: quota_error }
if (!response_generate_openai.headersSent) {
response_generate_openai.send({ error: true, quota_error: quota_error });
response_generate_openai.send(response);
} else if (!response_generate_openai.writableEnded) {
response_generate_openai.write(response);
}
}
@@ -3544,7 +3585,7 @@ function createTokenizationHandler(getTokenizerFn) {
app.post("/tokenize_llama", jsonParser, createTokenizationHandler(() => spp_llama));
app.post("/tokenize_nerdstash", jsonParser, createTokenizationHandler(() => spp_nerd));
app.post("/tokenize_nerdstash_v2", jsonParser, createTokenizationHandler(() => spp_nerd_v2));
app.post("/tokenize_via_api", jsonParser, async function(request, response) {
app.post("/tokenize_via_api", jsonParser, async function (request, response) {
if (!request.body) {
return response.sendStatus(400);
}
@@ -3552,7 +3593,7 @@ app.post("/tokenize_via_api", jsonParser, async function(request, response) {
try {
const args = {
body: JSON.stringify({"prompt": text}),
body: JSON.stringify({ "prompt": text }),
headers: { "Content-Type": "application/json" }
};
@@ -4542,22 +4583,22 @@ async function getManifest(extensionPath) {
}
async function checkIfRepoIsUpToDate(extensionPath) {
const git = simpleGit();
await git.cwd(extensionPath).fetch('origin');
const currentBranch = await git.cwd(extensionPath).branch();
const currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
const log = await git.cwd(extensionPath).log({
from: currentCommitHash,
to: `origin/${currentBranch.current}`,
});
const git = simpleGit();
await git.cwd(extensionPath).fetch('origin');
const currentBranch = await git.cwd(extensionPath).branch();
const currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
const log = await git.cwd(extensionPath).log({
from: currentCommitHash,
to: `origin/${currentBranch.current}`,
});
// Fetch remote repository information
const remotes = await git.cwd(extensionPath).getRemotes(true);
// Fetch remote repository information
const remotes = await git.cwd(extensionPath).getRemotes(true);
return {
isUpToDate: log.total === 0,
remoteUrl: remotes[0].refs.fetch, // URL of the remote repository
};
return {
isUpToDate: log.total === 0,
remoteUrl: remotes[0].refs.fetch, // URL of the remote repository
};
}
@@ -4672,7 +4713,7 @@ app.post('/get_extension_version', jsonParser, async (request, response) => {
const extensionPath = path.join(directories.extensions, 'third-party', extensionName);
if (!fs.existsSync(extensionPath)) {
return response.status(404).send(`Directory does not exist at ${extensionPath}`);
return response.status(404).send(`Directory does not exist at ${extensionPath}`);
}
const currentBranch = await git.cwd(extensionPath).branch();
@@ -4689,7 +4730,7 @@ app.post('/get_extension_version', jsonParser, async (request, response) => {
console.log('Getting extension version failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
}
}
}
);
/**