Merge branch 'SillyTavern:staging' into staging
This commit is contained in:
commit
844d9c86a4
|
@ -291,7 +291,6 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。
|
||||||
* RossAscends' additions: AGPL v3
|
* RossAscends' additions: AGPL v3
|
||||||
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
||||||
* kingbri's various commits and suggestions (https://github.com/bdashore3)
|
* kingbri's various commits and suggestions (https://github.com/bdashore3)
|
||||||
* BlipRanger's miscellaneous UI & extension modifications (https://github.com/BlipRanger)
|
|
||||||
* Waifu mode inspired by the work of PepperTaco (https://github.com/peppertaco/Tavern/)
|
* Waifu mode inspired by the work of PepperTaco (https://github.com/peppertaco/Tavern/)
|
||||||
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
||||||
* Thanks oobabooga for compiling presets for TextGen
|
* Thanks oobabooga for compiling presets for TextGen
|
||||||
|
|
|
@ -293,7 +293,6 @@ GNU Affero General Public License for more details.**
|
||||||
* RossAscends' additions: AGPL v3
|
* RossAscends' additions: AGPL v3
|
||||||
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
* Portions of CncAnon's TavernAITurbo mod: Unknown license
|
||||||
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
* kingbri's various commits and suggestions (<https://github.com/bdashore3>)
|
||||||
* BlipRanger's miscellaneous UI & extension modifications (<https://github.com/BlipRanger>)
|
|
||||||
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>)
|
||||||
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
* Thanks Pygmalion University for being awesome testers and suggesting cool features!
|
||||||
* Thanks oobabooga for compiling presets for TextGen
|
* Thanks oobabooga for compiling presets for TextGen
|
||||||
|
@ -306,3 +305,4 @@ GNU Affero General Public License for more details.**
|
||||||
* 10K Discord Users Celebratory Background by @kallmeflocc
|
* 10K Discord Users Celebratory Background by @kallmeflocc
|
||||||
* Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc
|
* Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc
|
||||||
* Korean translation by @doloroushyeonse
|
* Korean translation by @doloroushyeonse
|
||||||
|
* k_euler_a support for Horde by <https://github.com/Teashrock>
|
||||||
|
|
|
@ -26,4 +26,5 @@ secrets.json
|
||||||
/dist
|
/dist
|
||||||
/backups/
|
/backups/
|
||||||
public/movingUI/
|
public/movingUI/
|
||||||
|
public/QuickReplies/
|
||||||
content.log
|
content.log
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "sillytavern",
|
"name": "sillytavern",
|
||||||
"version": "1.9.2",
|
"version": "1.9.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sillytavern",
|
"name": "sillytavern",
|
||||||
"version": "1.9.2",
|
"version": "1.9.3",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dqbd/tiktoken": "^1.0.2",
|
"@dqbd/tiktoken": "^1.0.2",
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||||
},
|
},
|
||||||
"version": "1.9.2",
|
"version": "1.9.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"pkg": "pkg --compress Gzip --no-bytecode --public ."
|
"pkg": "pkg --compress Gzip --no-bytecode --public ."
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"order": [5, 0, 1, 3],
|
||||||
|
"temperature": 1.23,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 200,
|
||||||
|
"typical_p": 0.966,
|
||||||
|
"tail_free_sampling": 0.982,
|
||||||
|
"repetition_penalty": 1.74,
|
||||||
|
"repetition_penalty_range": 4000,
|
||||||
|
"repetition_penalty_frequency": 0,
|
||||||
|
"repetition_penalty_presence": 0.02,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "aggressive",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"order": [6, 0, 1, 2, 3],
|
||||||
|
"temperature": 1,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 25,
|
||||||
|
"top_p": 1,
|
||||||
|
"tail_free_sampling": 0.925,
|
||||||
|
"repetition_penalty": 1.6,
|
||||||
|
"repetition_penalty_frequency": 0.001,
|
||||||
|
"repetition_penalty_range": 0,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "medium",
|
||||||
|
"cfg_scale": 1.55,
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"order": [6, 2, 3, 1, 0],
|
||||||
|
"temperature": 1,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 0,
|
||||||
|
"top_p": 0.96,
|
||||||
|
"tail_free_sampling": 0.96,
|
||||||
|
"repetition_penalty": 2,
|
||||||
|
"repetition_penalty_slope": 1,
|
||||||
|
"repetition_penalty_frequency": 0.02,
|
||||||
|
"repetition_penalty_range": 0,
|
||||||
|
"repetition_penalty_presence": 0.3,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "very_aggressive",
|
||||||
|
"cfg_scale": 1.3,
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"order": [2, 3, 0, 4, 1],
|
||||||
|
"temperature": 1.35,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 12,
|
||||||
|
"top_p": 0.85,
|
||||||
|
"top_a": 0.1,
|
||||||
|
"tail_free_sampling": 0.915,
|
||||||
|
"repetition_penalty": 2.8,
|
||||||
|
"repetition_penalty_range": 2048,
|
||||||
|
"repetition_penalty_slope": 0.02,
|
||||||
|
"repetition_penalty_frequency": 0.02,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "aggressive",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"order": [4, 0, 5, 3, 2],
|
||||||
|
"temperature": 1.09,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_p": 0.969,
|
||||||
|
"top_a": 0.09,
|
||||||
|
"typical_p": 0.99,
|
||||||
|
"tail_free_sampling": 0.969,
|
||||||
|
"repetition_penalty": 1.09,
|
||||||
|
"repetition_penalty_range": 8192,
|
||||||
|
"repetition_penalty_slope": 0.069,
|
||||||
|
"repetition_penalty_frequency": 0.006,
|
||||||
|
"repetition_penalty_presence": 0.009,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "very_light",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"order": [6, 0, 1, 2, 3],
|
||||||
|
"temperature": 1,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 25,
|
||||||
|
"top_p": 1,
|
||||||
|
"tail_free_sampling": 0.925,
|
||||||
|
"repetition_penalty": 1.9,
|
||||||
|
"repetition_penalty_range": 768,
|
||||||
|
"repetition_penalty_slope": 1,
|
||||||
|
"repetition_penalty_frequency": 0.0025,
|
||||||
|
"repetition_penalty_presence": 0.001,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "off",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"order": [6, 1, 0, 5, 3],
|
||||||
|
"temperature": 1.25,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 70,
|
||||||
|
"typical_p": 0.9,
|
||||||
|
"tail_free_sampling": 0.925,
|
||||||
|
"repetition_penalty": 2,
|
||||||
|
"repetition_penalty_range": 1632,
|
||||||
|
"repetition_penalty_frequency": 0,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "aggressive",
|
||||||
|
"cfg_scale": 1.825,
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"order": [6, 0, 4, 1, 2, 5, 3],
|
||||||
|
"temperature": 1.31,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 25,
|
||||||
|
"top_p": 0.97,
|
||||||
|
"top_a": 0.18,
|
||||||
|
"typical_p": 0.98,
|
||||||
|
"tail_free_sampling": 1,
|
||||||
|
"repetition_penalty": 1.55,
|
||||||
|
"repetition_penalty_frequency": 0.00075,
|
||||||
|
"repetition_penalty_presence": 0.00085,
|
||||||
|
"repetition_penalty_range": 8192,
|
||||||
|
"repetition_penalty_slope": 1.8,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "medium",
|
||||||
|
"cfg_scale": 1.35,
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"order": [3, 0, 5],
|
||||||
|
"temperature": 2.5,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"typical_p": 0.966,
|
||||||
|
"tail_free_sampling": 0.933,
|
||||||
|
"repetition_penalty": 1,
|
||||||
|
"repetition_penalty_range": 2048,
|
||||||
|
"repetition_penalty_frequency": 0,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "aggressive",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -1,20 +1,21 @@
|
||||||
{
|
{
|
||||||
"order": [1, 3, 4, 0, 2],
|
"order": [1, 5, 0, 2, 3, 4],
|
||||||
"temperature": 1.05,
|
"temperature": 1.5,
|
||||||
"max_length": 40,
|
"max_length": 300,
|
||||||
"min_length": 1,
|
"min_length": 1,
|
||||||
"top_k": 79,
|
"top_k": 10,
|
||||||
"top_p": 0.95,
|
"top_p": 0.75,
|
||||||
"top_a": 0.075,
|
"top_a": 0.08,
|
||||||
"typical_p": 1,
|
"typical_p": 0.975,
|
||||||
"tail_free_sampling": 0.989,
|
"tail_free_sampling": 0.967,
|
||||||
"repetition_penalty": 1.5,
|
"repetition_penalty": 2.25,
|
||||||
"repetition_penalty_range": 8192,
|
"repetition_penalty_range": 8192,
|
||||||
"repetition_penalty_slope": 3.33,
|
"repetition_penalty_slope": 0.09,
|
||||||
"repetition_penalty_frequency": 0.03,
|
"repetition_penalty_frequency": 0,
|
||||||
"repetition_penalty_presence": 0.005,
|
"repetition_penalty_presence": 0.005,
|
||||||
"use_cache": false,
|
"use_cache": false,
|
||||||
"return_full_text": false,
|
"return_full_text": false,
|
||||||
"prefix": "vanilla",
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "very_light",
|
||||||
"max_context": 8192
|
"max_context": 8192
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"order": [6, 0, 5],
|
||||||
|
"temperature": 0.895,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"typical_p": 0.9,
|
||||||
|
"repetition_penalty": 2,
|
||||||
|
"repetition_penalty_slope": 3.2,
|
||||||
|
"repetition_penalty_frequency": 0,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"repetition_penalty_range": 4048,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "aggressive",
|
||||||
|
"cfg_scale": 1.3,
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"order": [6, 1, 0, 5, 3, 2],
|
||||||
|
"temperature": 1.5,
|
||||||
|
"max_length": 300,
|
||||||
|
"min_length": 1,
|
||||||
|
"top_k": 70,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"typical_p": 0.95,
|
||||||
|
"tail_free_sampling": 0.95,
|
||||||
|
"repetition_penalty": 1.6,
|
||||||
|
"repetition_penalty_range": 2016,
|
||||||
|
"repetition_penalty_frequency": 0,
|
||||||
|
"repetition_penalty_presence": 0,
|
||||||
|
"use_cache": false,
|
||||||
|
"return_full_text": false,
|
||||||
|
"prefix": "vanilla",
|
||||||
|
"phrase_rep_pen": "very_aggressive",
|
||||||
|
"max_context": 8192
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "Default",
|
||||||
|
"quickReplyEnabled": true,
|
||||||
|
"quickReplySlots": [
|
||||||
|
{
|
||||||
|
"mes": "/?",
|
||||||
|
"label": "HELP",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mes": "/newchat",
|
||||||
|
"label": "New Chat",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mes": "/bgcol",
|
||||||
|
"label": "Match UI to Background",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfSlots": 3,
|
||||||
|
"selectedPreset": "Default"
|
||||||
|
}
|
|
@ -545,6 +545,19 @@
|
||||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" />
|
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" maxlength="100" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="range-block" data-source="openai,claude">
|
||||||
|
<div class="range-block-title justifyLeft" data-i18n="Proxy Password">
|
||||||
|
Proxy Password
|
||||||
|
</div>
|
||||||
|
<div class="toggle-description justifyLeft">
|
||||||
|
<span data-i18n="Will be used as a password for the proxy instead of API key.">
|
||||||
|
Will be used as a password for the proxy instead of API key.<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<input id="openai_proxy_password" type="text" class="text_pole" placeholder="" maxlength="200" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="range-block" data-source="openai,claude">
|
<div class="range-block" data-source="openai,claude">
|
||||||
<div class="range-block-title justifyLeft">
|
<div class="range-block-title justifyLeft">
|
||||||
<label for="legacy_streaming" class="checkbox_label">
|
<label for="legacy_streaming" class="checkbox_label">
|
||||||
|
@ -670,6 +683,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="range-block" data-source="claude">
|
||||||
|
<div class="range-block-title" data-i18n="Assistant Prefill">
|
||||||
|
Assistant Prefill
|
||||||
|
</div>
|
||||||
|
<div class="wide100p">
|
||||||
|
<input type="text" id="claude_assistant_prefill" class="text_pole" placeholder="Start Claude's answer with...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
@ -854,6 +875,36 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="range-block">
|
||||||
|
<div class="range-block-title" data-i18n="CFG Scale">
|
||||||
|
CFG Scale
|
||||||
|
</div>
|
||||||
|
<div class="range-block-range-and-counter">
|
||||||
|
<div class="range-block-range">
|
||||||
|
<input type="range" id="cfg_scale_novel" name="volume" min="1" max="3" step="0.05">
|
||||||
|
</div>
|
||||||
|
<div class="range-block-counter">
|
||||||
|
<div contenteditable="true" data-for="cfg_scale_novel" id="cfg_scale_counter_novel">
|
||||||
|
select
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="range-block">
|
||||||
|
<div class="range-block-title" data-i18n="Phrase Repetition Penalty">
|
||||||
|
Phrase Repetition Penalty
|
||||||
|
</div>
|
||||||
|
<div class="range-block-range-and-counter">
|
||||||
|
<div class="range-block-range">
|
||||||
|
<input type="range" id="phrase_rep_pen_novel" name="volume" min="0" max="5" step="1">
|
||||||
|
</div>
|
||||||
|
<div class="range-block-counter">
|
||||||
|
<div contenteditable="true" data-for="phrase_rep_pen_novel" id="phrase_rep_pen_counter_novel">
|
||||||
|
select
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="range-block">
|
<div class="range-block">
|
||||||
<div class="range-block-title" data-i18n="Min Length">
|
<div class="range-block-title" data-i18n="Min Length">
|
||||||
Min Length
|
Min Length
|
||||||
|
@ -1479,6 +1530,7 @@
|
||||||
<option value="euterpe-v2">Euterpe</option>
|
<option value="euterpe-v2">Euterpe</option>
|
||||||
<option value="krake-v2">Krake</option>
|
<option value="krake-v2">Krake</option>
|
||||||
<option value="clio-v1">Clio</option>
|
<option value="clio-v1">Clio</option>
|
||||||
|
<option value="kayra-v1">Kayra</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
<div id="online_status3">
|
<div id="online_status3">
|
||||||
|
@ -1550,6 +1602,11 @@
|
||||||
<input id="api_key_openai" name="api_key_openai" class="text_pole flex1" maxlength="500" value="" type="text" autocomplete="off">
|
<input id="api_key_openai" name="api_key_openai" class="text_pole flex1" maxlength="500" value="" type="text" autocomplete="off">
|
||||||
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_openai"></div>
|
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_openai"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="ReverseProxyWarningMessage2" class="reverse_proxy_warning">
|
||||||
|
<b data-i18n="Use Proxy password field instead. This input will be ignored.">
|
||||||
|
Use "Proxy password" field instead. This input will be ignored.
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
<div data-for="api_key_openai" class="neutral_warning">
|
<div data-for="api_key_openai" class="neutral_warning">
|
||||||
For privacy reasons, your API key will be hidden after you reload the page.
|
For privacy reasons, your API key will be hidden after you reload the page.
|
||||||
</div>
|
</div>
|
||||||
|
@ -1867,8 +1924,8 @@
|
||||||
<option value="1">GPT-3 (OpenAI)</option>
|
<option value="1">GPT-3 (OpenAI)</option>
|
||||||
<option value="2">GPT-3 (Alternative / Classic)</option>
|
<option value="2">GPT-3 (Alternative / Classic)</option>
|
||||||
<option value="3">Sentencepiece (LLaMA)</option>
|
<option value="3">Sentencepiece (LLaMA)</option>
|
||||||
<option value="4">NerdStash (NovelAI Krake)</option>
|
<option value="4">NerdStash (NovelAI Clio)</option>
|
||||||
<option value="5">NerdStash v2 (NovelAI Clio)</option>
|
<option value="5">NerdStash v2 (NovelAI Kayra)</option>
|
||||||
<option value="6">API (WebUI)</option>
|
<option value="6">API (WebUI)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2372,6 +2429,10 @@
|
||||||
<input id="swipes-checkbox" type="checkbox" />
|
<input id="swipes-checkbox" type="checkbox" />
|
||||||
<span data-i18n="Swipes">Swipes</span>
|
<span data-i18n="Swipes">Swipes</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label for="fuzzy_search_checkbox">
|
||||||
|
<input id="fuzzy_search_checkbox" type="checkbox" />
|
||||||
|
<span data-i18n="Advanced Character Search">Advanced Character Search</span>
|
||||||
|
</label>
|
||||||
<label for="prefer_character_prompt" title="If checked and the character card contains a prompt override (System Prompt), use that instead." data-i18n="[title]If checked and the character card contains a prompt override (System Prompt), use that instead." class="checkbox_label">
|
<label for="prefer_character_prompt" title="If checked and the character card contains a prompt override (System Prompt), use that instead." data-i18n="[title]If checked and the character card contains a prompt override (System Prompt), use that instead." class="checkbox_label">
|
||||||
<input id="prefer_character_prompt" type="checkbox" />
|
<input id="prefer_character_prompt" type="checkbox" />
|
||||||
<span data-i18n="Prefer Character Card Prompt">Prefer Char. Prompt</span>
|
<span data-i18n="Prefer Character Card Prompt">Prefer Char. Prompt</span>
|
||||||
|
@ -2872,6 +2933,8 @@
|
||||||
<option data-field="date_last_chat" data-order="desc" data-i18n="Recent">Recent</option>
|
<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="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="chat_size" data-order="asc" data-i18n="Least chats">Least chats</option>
|
||||||
|
<option data-field="data_size" data-order="desc" data-i18n="Most tokens">Most tokens</option>
|
||||||
|
<option data-field="data_size" data-order="asc" data-i18n="Least tokens">Least tokens</option>
|
||||||
<option data-field="name" data-order="random" data-i18n="Random">Random</option>
|
<option data-field="name" data-order="random" data-i18n="Random">Random</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
|
@ -3392,7 +3455,8 @@
|
||||||
<div class="flex-container wide100pLess70px character_select_container">
|
<div class="flex-container wide100pLess70px character_select_container">
|
||||||
<div class="wide100p character_name_block">
|
<div class="wide100p character_name_block">
|
||||||
<span class="ch_name"></span>
|
<span class="ch_name"></span>
|
||||||
<i class="ch_avatar_url"></i>
|
<small class="character_version"></small>
|
||||||
|
<small class="ch_avatar_url"></small>
|
||||||
</div>
|
</div>
|
||||||
<i class="ch_fav_icon fa-solid fa-star"></i>
|
<i class="ch_fav_icon fa-solid fa-star"></i>
|
||||||
<input class="ch_fav" value="" hidden />
|
<input class="ch_fav" value="" hidden />
|
||||||
|
|
|
@ -76,6 +76,7 @@ import {
|
||||||
persona_description_positions,
|
persona_description_positions,
|
||||||
loadMovingUIState,
|
loadMovingUIState,
|
||||||
getCustomStoppingStrings,
|
getCustomStoppingStrings,
|
||||||
|
fuzzySearchCharacters,
|
||||||
MAX_CONTEXT_DEFAULT,
|
MAX_CONTEXT_DEFAULT,
|
||||||
} from "./scripts/power-user.js";
|
} from "./scripts/power-user.js";
|
||||||
|
|
||||||
|
@ -736,6 +737,9 @@ let token;
|
||||||
|
|
||||||
var PromptArrayItemForRawPromptDisplay;
|
var PromptArrayItemForRawPromptDisplay;
|
||||||
|
|
||||||
|
export let active_character = ""
|
||||||
|
export let active_group = ""
|
||||||
|
|
||||||
export function getRequestHeaders() {
|
export function getRequestHeaders() {
|
||||||
return {
|
return {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -785,6 +789,14 @@ function checkOnlineStatus() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setActiveCharacter(character) {
|
||||||
|
active_character = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setActiveGroup(group) {
|
||||||
|
active_group = group;
|
||||||
|
}
|
||||||
|
|
||||||
async function getStatus() {
|
async function getStatus() {
|
||||||
if (is_get_status) {
|
if (is_get_status) {
|
||||||
if (main_api == "koboldhorde") {
|
if (main_api == "koboldhorde") {
|
||||||
|
@ -896,6 +908,14 @@ async function printCharacters() {
|
||||||
template.find('.ch_description').hide();
|
template.find('.ch_description').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const version = item.data?.character_version || '';
|
||||||
|
if (version) {
|
||||||
|
template.find('.character_version').text(version);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
template.find('.character_version').hide();
|
||||||
|
}
|
||||||
|
|
||||||
// Display inline tags
|
// Display inline tags
|
||||||
const tags = getTagsList(item.avatar);
|
const tags = getTagsList(item.avatar);
|
||||||
const tagsElement = template.find('.tags');
|
const tagsElement = template.find('.tags');
|
||||||
|
@ -3055,9 +3075,9 @@ function getMaxContextSize() {
|
||||||
// Should be used with nerdstash tokenizer for best results
|
// Should be used with nerdstash tokenizer for best results
|
||||||
this_max_context = Math.min(max_context, 2048);
|
this_max_context = Math.min(max_context, 2048);
|
||||||
}
|
}
|
||||||
if (nai_settings.model_novel == 'clio-v1') {
|
if (nai_settings.model_novel == 'clio-v1' || nai_settings.model_novel == 'kayra-v1') {
|
||||||
// Clio has a max context of 8192
|
// Clio and Kayra has a max context of 8192
|
||||||
// Should be used with nerdstash_v2 tokenizer for best results
|
// Should be used with nerdstash / nerdstash_v2 tokenizer for best results
|
||||||
this_max_context = Math.min(max_context, 8192);
|
this_max_context = Math.min(max_context, 8192);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5000,6 +5020,10 @@ async function getSettings(type) {
|
||||||
highlightSelectedAvatar();
|
highlightSelectedAvatar();
|
||||||
setPersonaDescription();
|
setPersonaDescription();
|
||||||
|
|
||||||
|
//Load the active character and group
|
||||||
|
active_character = settings.active_character;
|
||||||
|
active_group = settings.active_group;
|
||||||
|
|
||||||
//Load the API server URL from settings
|
//Load the API server URL from settings
|
||||||
api_server = settings.api_server;
|
api_server = settings.api_server;
|
||||||
$("#api_url_text").val(api_server);
|
$("#api_url_text").val(api_server);
|
||||||
|
@ -5040,6 +5064,8 @@ async function saveSettings(type) {
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
firstRun: firstRun,
|
firstRun: firstRun,
|
||||||
username: name1,
|
username: name1,
|
||||||
|
active_character: active_character,
|
||||||
|
active_group: active_group,
|
||||||
api_server: api_server,
|
api_server: api_server,
|
||||||
api_server_textgenerationwebui: api_server_textgenerationwebui,
|
api_server_textgenerationwebui: api_server_textgenerationwebui,
|
||||||
preset_settings: preset_settings,
|
preset_settings: preset_settings,
|
||||||
|
@ -7029,18 +7055,26 @@ $(document).ready(function () {
|
||||||
$("#character_search_bar").on("input", function () {
|
$("#character_search_bar").on("input", function () {
|
||||||
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
|
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
|
||||||
const searchValue = $(this).val().trim().toLowerCase();
|
const searchValue = $(this).val().trim().toLowerCase();
|
||||||
|
const fuzzySearchResults = power_user.fuzzy_search ? fuzzySearchCharacters(searchValue) : [];
|
||||||
|
|
||||||
|
function getIsValidSearch(_this) {
|
||||||
|
const name = $(_this).find(".ch_name").text().toLowerCase();
|
||||||
|
const chid = $(_this).attr("chid");
|
||||||
|
|
||||||
|
if (power_user.fuzzy_search) {
|
||||||
|
return fuzzySearchResults.includes(parseInt(chid));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return name.includes(searchValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!searchValue) {
|
if (!searchValue) {
|
||||||
$(selector).removeClass('hiddenBySearch');
|
$(selector).removeClass('hiddenBySearch');
|
||||||
updateVisibleDivs('#rm_print_characters_block', true);
|
updateVisibleDivs('#rm_print_characters_block', true);
|
||||||
} else {
|
} else {
|
||||||
$(selector).each(function () {
|
$(selector).each(function () {
|
||||||
const isValidSearch = $(this)
|
const isValidSearch = getIsValidSearch(this);
|
||||||
.find(".ch_name")
|
|
||||||
.text()
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchValue);
|
|
||||||
|
|
||||||
$(this).toggleClass('hiddenBySearch', !isValidSearch);
|
$(this).toggleClass('hiddenBySearch', !isValidSearch);
|
||||||
});
|
});
|
||||||
updateVisibleDivs('#rm_print_characters_block', true);
|
updateVisibleDivs('#rm_print_characters_block', true);
|
||||||
|
@ -7226,7 +7260,7 @@ $(document).ready(function () {
|
||||||
const data = { old_bg, new_bg };
|
const data = { old_bg, new_bg };
|
||||||
const response = await fetch('/renamebackground', {
|
const response = await fetch('/renamebackground', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers:getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
});
|
});
|
||||||
|
@ -7981,7 +8015,7 @@ $(document).ready(function () {
|
||||||
.append(
|
.append(
|
||||||
`<textarea id='curEditTextarea' class='edit_textarea' style='max-width:auto;'></textarea>`
|
`<textarea id='curEditTextarea' class='edit_textarea' style='max-width:auto;'></textarea>`
|
||||||
);
|
);
|
||||||
$('#curEditTextarea').val(text);
|
$('#curEditTextarea').val(text);
|
||||||
let edit_textarea = $(this)
|
let edit_textarea = $(this)
|
||||||
.closest(".mes_block")
|
.closest(".mes_block")
|
||||||
.find(".edit_textarea");
|
.find(".edit_textarea");
|
||||||
|
|
|
@ -13,6 +13,10 @@ import {
|
||||||
menu_type,
|
menu_type,
|
||||||
max_context,
|
max_context,
|
||||||
saveSettingsDebounced,
|
saveSettingsDebounced,
|
||||||
|
active_group,
|
||||||
|
active_character,
|
||||||
|
setActiveGroup,
|
||||||
|
setActiveCharacter,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -239,7 +243,6 @@ $("#rm_ch_create_block").on("input", function () { countTokensDebounced(); });
|
||||||
$("#character_popup").on("input", function () { countTokensDebounced(); });
|
$("#character_popup").on("input", function () { countTokensDebounced(); });
|
||||||
//function:
|
//function:
|
||||||
export function RA_CountCharTokens() {
|
export function RA_CountCharTokens() {
|
||||||
$("#result_info").html("");
|
|
||||||
//console.log('RA_TC -- starting with this_chid = ' + this_chid);
|
//console.log('RA_TC -- starting with this_chid = ' + this_chid);
|
||||||
if (menu_type === "create") { //if new char
|
if (menu_type === "create") { //if new char
|
||||||
function saveFormVariables() {
|
function saveFormVariables() {
|
||||||
|
@ -331,11 +334,21 @@ export function RA_CountCharTokens() {
|
||||||
characterStatsHandler(characters, this_chid);
|
characterStatsHandler(characters, this_chid);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//Auto Load Last Charcter -- (fires when active_character is defined and auto_load_chat is true)
|
/**
|
||||||
|
* Auto load chat with the last active character or group.
|
||||||
|
* Fires when active_character is defined and auto_load_chat is true.
|
||||||
|
* The function first tries to find a character with a specific ID from the global settings.
|
||||||
|
* If it doesn't exist, it tries to find a group with a specific grid from the global settings.
|
||||||
|
* If the character list hadn't been loaded yet, it calls itself again after 100ms delay.
|
||||||
|
* The character or group is selected (clicked) if it is found.
|
||||||
|
*/
|
||||||
async function RA_autoloadchat() {
|
async function RA_autoloadchat() {
|
||||||
if (document.getElementById('CharID0') !== null) {
|
if (document.getElementById('CharID0') !== null) {
|
||||||
var charToAutoLoad = document.getElementById('CharID' + LoadLocal('ActiveChar'));
|
// active character is the name, we should look it up in the character list and get the id
|
||||||
let groupToAutoLoad = document.querySelector(`.group_select[grid="${LoadLocal('ActiveGroup')}"]`);
|
let active_character_id = Object.keys(characters).find(key => characters[key].avatar === active_character);
|
||||||
|
|
||||||
|
var charToAutoLoad = document.getElementById('CharID' + active_character_id);
|
||||||
|
let groupToAutoLoad = document.querySelector(`.group_select[grid="${active_group}"]`);
|
||||||
if (charToAutoLoad != null) {
|
if (charToAutoLoad != null) {
|
||||||
$(charToAutoLoad).click();
|
$(charToAutoLoad).click();
|
||||||
}
|
}
|
||||||
|
@ -343,7 +356,7 @@ async function RA_autoloadchat() {
|
||||||
$(groupToAutoLoad).click();
|
$(groupToAutoLoad).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the charcter list hadn't been loaded yet, try again.
|
// if the character list hadn't been loaded yet, try again.
|
||||||
} else { setTimeout(RA_autoloadchat, 100); }
|
} else { setTimeout(RA_autoloadchat, 100); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,8 +461,8 @@ function RA_autoconnect(PrevApi) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'openai':
|
case 'openai':
|
||||||
if ((secret_state[SECRET_KEYS.OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.OPENAI)
|
if (((secret_state[SECRET_KEYS.OPENAI] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.OPENAI)
|
||||||
|| (secret_state[SECRET_KEYS.CLAUDE] && oai_settings.chat_completion_source == chat_completion_sources.CLAUDE)
|
|| ((secret_state[SECRET_KEYS.CLAUDE] || oai_settings.reverse_proxy) && oai_settings.chat_completion_source == chat_completion_sources.CLAUDE)
|
||||||
|| (secret_state[SECRET_KEYS.SCALE] && oai_settings.chat_completion_source == chat_completion_sources.SCALE)
|
|| (secret_state[SECRET_KEYS.SCALE] && oai_settings.chat_completion_source == chat_completion_sources.SCALE)
|
||||||
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
|| (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI)
|
||||||
|| (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
|
|| (secret_state[SECRET_KEYS.OPENROUTER] && oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER)
|
||||||
|
@ -904,16 +917,22 @@ $("document").ready(function () {
|
||||||
$("#rm_button_characters").click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); });
|
$("#rm_button_characters").click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); });
|
||||||
|
|
||||||
// when a char is selected from the list, save them as the auto-load character for next page load
|
// when a char is selected from the list, save them as the auto-load character for next page load
|
||||||
|
|
||||||
|
// when a char is selected from the list, save their name as the auto-load character for next page load
|
||||||
$(document).on("click", ".character_select", function () {
|
$(document).on("click", ".character_select", function () {
|
||||||
SaveLocal('ActiveChar', $(this).attr('chid'));
|
setActiveCharacter($(this).find('.avatar').attr('title'));
|
||||||
SaveLocal('ActiveGroup', null);
|
setActiveGroup(null);
|
||||||
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", ".group_select", function () {
|
$(document).on("click", ".group_select", function () {
|
||||||
SaveLocal('ActiveChar', null);
|
setActiveCharacter(null);
|
||||||
SaveLocal('ActiveGroup', $(this).data('id'));
|
setActiveGroup($(this).data('id'));
|
||||||
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
//this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height)
|
||||||
$('#send_textarea').on('input', function () {
|
$('#send_textarea').on('input', function () {
|
||||||
this.style.height = '40px';
|
this.style.height = '40px';
|
||||||
|
|
|
@ -73,6 +73,7 @@ const extension_settings = {
|
||||||
fluctuation: 0.1,
|
fluctuation: 0.1,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
speech_recognition: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let modules = [];
|
let modules = [];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { chat_metadata, callPopup, saveSettingsDebounced } from "../../../script.js";
|
import { chat_metadata, callPopup, saveSettingsDebounced, getCurrentChatId } from "../../../script.js";
|
||||||
import { getContext, extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
import { getContext, extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
||||||
import {
|
import {
|
||||||
substituteParams,
|
substituteParams,
|
||||||
|
@ -11,9 +11,10 @@ import { registerSlashCommand } from "../../slash-commands.js";
|
||||||
const MODULE_NAME = "Objective"
|
const MODULE_NAME = "Objective"
|
||||||
|
|
||||||
|
|
||||||
let globalObjective = ""
|
let taskTree = null
|
||||||
let globalTasks = []
|
let globalTasks = []
|
||||||
let currentChatId = ""
|
let currentChatId = ""
|
||||||
|
let currentObjective = null
|
||||||
let currentTask = null
|
let currentTask = null
|
||||||
let checkCounter = 0
|
let checkCounter = 0
|
||||||
|
|
||||||
|
@ -47,56 +48,48 @@ let objectivePrompts = defaultPrompts
|
||||||
//# Task Management #//
|
//# Task Management #//
|
||||||
//###############################//
|
//###############################//
|
||||||
|
|
||||||
// Accepts optional index. Defaults to adding to end of list.
|
|
||||||
function addTask(description, index = null) {
|
|
||||||
index = index != null ? index: index = globalTasks.length
|
|
||||||
globalTasks.splice(index, 0, new ObjectiveTask(
|
|
||||||
{description: description}
|
|
||||||
))
|
|
||||||
saveState()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the task and index or throw an error
|
// Return the task and index or throw an error
|
||||||
function getTaskById(taskId){
|
function getTaskById(taskId){
|
||||||
if (taskId == null) {
|
if (taskId == null) {
|
||||||
throw `Null task id`
|
throw `Null task id`
|
||||||
}
|
}
|
||||||
const index = globalTasks.findIndex((task) => task.id === taskId);
|
return getTaskByIdRecurse(taskId, taskTree)
|
||||||
if (index !== -1) {
|
|
||||||
return { task: globalTasks[index], index: index };
|
|
||||||
} else {
|
|
||||||
throw `Cannot find task with ${taskId}`
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteTask(taskId){
|
function getTaskByIdRecurse(taskId, task) {
|
||||||
const { task, index } = getTaskById(taskId)
|
if (task.id == taskId){
|
||||||
|
return task
|
||||||
globalTasks.splice(index, 1)
|
}
|
||||||
setCurrentTask()
|
for (const childTask of task.children) {
|
||||||
updateUiTaskList()
|
const foundTask = getTaskByIdRecurse(taskId, childTask);
|
||||||
|
if (foundTask != null) {
|
||||||
|
return foundTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much.
|
// Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much.
|
||||||
async function generateTasks() {
|
async function generateTasks() {
|
||||||
const prompt = substituteParams(objectivePrompts.createTask.replace(/{{objective}}/gi, globalObjective));
|
|
||||||
|
const prompt = substituteParams(objectivePrompts.createTask.replace(/{{objective}}/gi, currentObjective.description));
|
||||||
console.log(`Generating tasks for objective with prompt`)
|
console.log(`Generating tasks for objective with prompt`)
|
||||||
toastr.info('Generating tasks for objective', 'Please wait...');
|
toastr.info('Generating tasks for objective', 'Please wait...');
|
||||||
const taskResponse = await generateQuietPrompt(prompt)
|
const taskResponse = await generateQuietPrompt(prompt)
|
||||||
|
|
||||||
// Clear all existing global tasks when generating
|
// Clear all existing objective tasks when generating
|
||||||
globalTasks = []
|
currentObjective.children = []
|
||||||
const numberedListPattern = /^\d+\./
|
const numberedListPattern = /^\d+\./
|
||||||
|
|
||||||
// Create tasks from generated task list
|
// Create tasks from generated task list
|
||||||
for (const task of taskResponse.split('\n').map(x => x.trim())) {
|
for (const task of taskResponse.split('\n').map(x => x.trim())) {
|
||||||
if (task.match(numberedListPattern) != null) {
|
if (task.match(numberedListPattern) != null) {
|
||||||
addTask(task.replace(numberedListPattern,"").trim())
|
currentObjective.addTask(task.replace(numberedListPattern,"").trim())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateUiTaskList()
|
updateUiTaskList();
|
||||||
console.info(`Response for Objective: '${globalObjective}' was \n'${taskResponse}', \nwhich created tasks \n${JSON.stringify(globalTasks.map(v => {return v.toSaveState()}), null, 2)} `)
|
setCurrentTask();
|
||||||
|
console.info(`Response for Objective: '${taskTree.description}' was \n'${taskResponse}', \nwhich created tasks \n${JSON.stringify(globalTasks.map(v => {return v.toSaveState()}), null, 2)} `)
|
||||||
toastr.success(`Generated ${globalTasks.length} tasks`, 'Done!');
|
toastr.success(`Generated ${globalTasks.length} tasks`, 'Done!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +106,7 @@ async function checkTaskCompleted() {
|
||||||
|
|
||||||
// Check response if task complete
|
// Check response if task complete
|
||||||
if (taskResponse.includes("true")) {
|
if (taskResponse.includes("true")) {
|
||||||
console.info(`Character determined task '${JSON.stringify(currentTask.toSaveState())} is completed.`)
|
console.info(`Character determined task '${currentTask.description} is completed.`)
|
||||||
currentTask.completeTask()
|
currentTask.completeTask()
|
||||||
} else if (!(taskResponse.includes("false"))) {
|
} else if (!(taskResponse.includes("false"))) {
|
||||||
console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`)
|
console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`)
|
||||||
|
@ -122,29 +115,56 @@ async function checkTaskCompleted() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNextIncompleteTaskRecurse(task){
|
||||||
|
// Skip tasks with children, as they will have subtasks to determine completeness
|
||||||
|
if (task.completed === false && task.children.length === 0){
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
for (const childTask of task.children) {
|
||||||
|
if (childTask.completed === true){ // Don't recurse into completed tasks
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const foundTask = getNextIncompleteTaskRecurse(childTask);
|
||||||
|
if (foundTask != null) {
|
||||||
|
return foundTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Set a task in extensionPrompt context. Defaults to first incomplete
|
// Set a task in extensionPrompt context. Defaults to first incomplete
|
||||||
function setCurrentTask(taskId = null) {
|
function setCurrentTask(taskId = null) {
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
|
|
||||||
|
// TODO: Should probably null this rather than set empty object
|
||||||
currentTask = {};
|
currentTask = {};
|
||||||
|
|
||||||
// Set current task to either the next incomplete task, or the index
|
// Find the task, either next incomplete, or by provided taskId
|
||||||
if (taskId === null) {
|
if (taskId === null) {
|
||||||
currentTask = globalTasks.find(task => !task.completed) || {};
|
currentTask = getNextIncompleteTaskRecurse(taskTree) || {};
|
||||||
} else {
|
} else {
|
||||||
const { _, index } = getTaskById(taskId)
|
currentTask = getTaskById(taskId);
|
||||||
currentTask = globalTasks[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the task description and add to extension prompt
|
// Don't just check for a current task, check if it has data
|
||||||
const description = currentTask.description || null;
|
const description = currentTask.description || null;
|
||||||
|
|
||||||
// Now update the extension prompt
|
|
||||||
|
|
||||||
if (description) {
|
if (description) {
|
||||||
const extensionPromptText = objectivePrompts.currentTask.replace(/{{task}}/gi, description);
|
const extensionPromptText = objectivePrompts.currentTask.replace(/{{task}}/gi, description);
|
||||||
$('.objective-task').css({'border-color':'','border-width':''}) // Clear highlights
|
|
||||||
currentTask.descriptionSpan.css({'border-color':'yellow','border-width':'2px'}); // Highlight current task
|
// Remove highlights
|
||||||
|
$('.objective-task').css({'border-color':'','border-width':''})
|
||||||
|
// Highlight current task
|
||||||
|
let highlightTask = currentTask
|
||||||
|
while (highlightTask.parentId !== ""){
|
||||||
|
if (highlightTask.descriptionSpan){
|
||||||
|
highlightTask.descriptionSpan.css({'border-color':'yellow','border-width':'2px'});
|
||||||
|
}
|
||||||
|
const parent = getTaskById(highlightTask.parentId)
|
||||||
|
highlightTask = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Update the extension prompt
|
||||||
context.setExtensionPrompt(MODULE_NAME, extensionPromptText, 1, $('#objective-chat-depth').val());
|
context.setExtensionPrompt(MODULE_NAME, extensionPromptText, 1, $('#objective-chat-depth').val());
|
||||||
console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`);
|
console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -155,22 +175,26 @@ function setCurrentTask(taskId = null) {
|
||||||
saveState();
|
saveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskIdCounter = 0
|
function getHighestTaskIdRecurse(task) {
|
||||||
function getNextTaskId(){
|
let nextId = task.id;
|
||||||
// Make sure id does not exist
|
|
||||||
while (globalTasks.find(task => task.id == taskIdCounter) != undefined) {
|
for (const childTask of task.children) {
|
||||||
taskIdCounter += 1
|
const childId = getHighestTaskIdRecurse(childTask);
|
||||||
|
if (childId > nextId) {
|
||||||
|
nextId = childId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const nextId = taskIdCounter
|
return nextId;
|
||||||
console.log(`TaskID assigned: ${nextId}`)
|
|
||||||
taskIdCounter += 1
|
|
||||||
return nextId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//###############################//
|
||||||
|
//# Task Class #//
|
||||||
|
//###############################//
|
||||||
class ObjectiveTask {
|
class ObjectiveTask {
|
||||||
id
|
id
|
||||||
description
|
description
|
||||||
completed
|
completed
|
||||||
parent
|
parentId
|
||||||
children
|
children
|
||||||
|
|
||||||
// UI Elements
|
// UI Elements
|
||||||
|
@ -180,25 +204,67 @@ class ObjectiveTask {
|
||||||
deleteTaskButton
|
deleteTaskButton
|
||||||
addTaskButton
|
addTaskButton
|
||||||
|
|
||||||
constructor ({id=undefined, description, completed=false, parent=null}) {
|
constructor ({id=undefined, description, completed=false, parentId=""}) {
|
||||||
this.description = description
|
this.description = description
|
||||||
this.parent = parent
|
this.parentId = parentId
|
||||||
this.children = []
|
this.children = []
|
||||||
this.completed = completed
|
this.completed = completed
|
||||||
|
|
||||||
// Generate a new ID if none specified
|
// Generate a new ID if none specified
|
||||||
if (id==undefined){
|
if (id==undefined){
|
||||||
this.id = getNextTaskId()
|
this.id = getHighestTaskIdRecurse(taskTree) + 1
|
||||||
} else {
|
} else {
|
||||||
this.id=id
|
this.id=id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts optional index. Defaults to adding to end of list.
|
||||||
|
addTask(description, index = null) {
|
||||||
|
index = index != null ? index: index = this.children.length
|
||||||
|
this.children.splice(index, 0, new ObjectiveTask(
|
||||||
|
{description: description, parentId: this.id}
|
||||||
|
))
|
||||||
|
saveState()
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndex(){
|
||||||
|
if (this.parentId !== null) {
|
||||||
|
const parent = getTaskById(this.parentId)
|
||||||
|
const index = parent.children.findIndex(task => task.id === this.id)
|
||||||
|
if (index === -1){
|
||||||
|
throw `getIndex failed: Task '${this.description}' not found in parent task '${parent.description}'`
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
} else {
|
||||||
|
throw `getIndex failed: Task '${this.description}' has no parent`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to set parent to complete when all child tasks are completed
|
||||||
|
checkParentComplete() {
|
||||||
|
let all_completed = true;
|
||||||
|
if (this.parentId !== ""){
|
||||||
|
const parent = getTaskById(this.parentId);
|
||||||
|
for (const child of parent.children){
|
||||||
|
if (!child.completed){
|
||||||
|
all_completed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_completed){
|
||||||
|
parent.completed = true;
|
||||||
|
console.info(`Parent task '${parent.description}' completed after all child tasks complated.`)
|
||||||
|
} else {
|
||||||
|
parent.completed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Complete the current task, setting next task to next incomplete task
|
// Complete the current task, setting next task to next incomplete task
|
||||||
completeTask() {
|
completeTask() {
|
||||||
this.completed = true
|
this.completed = true
|
||||||
console.info(`Task successfully completed: ${JSON.stringify(this.description)}`)
|
console.info(`Task successfully completed: ${JSON.stringify(this.description)}`)
|
||||||
|
this.checkParentComplete()
|
||||||
setCurrentTask()
|
setCurrentTask()
|
||||||
updateUiTaskList()
|
updateUiTaskList()
|
||||||
}
|
}
|
||||||
|
@ -211,6 +277,7 @@ class ObjectiveTask {
|
||||||
<span class="text_pole objective-task" style="display: block" id="objective-task-description-${this.id}" contenteditable>${this.description}</span>
|
<span class="text_pole objective-task" style="display: block" id="objective-task-description-${this.id}" contenteditable>${this.description}</span>
|
||||||
<div id="objective-task-delete-${this.id}" class="objective-task-button fa-solid fa-xmark fa-2x" title="Delete Task"></div>
|
<div id="objective-task-delete-${this.id}" class="objective-task-button fa-solid fa-xmark fa-2x" title="Delete Task"></div>
|
||||||
<div id="objective-task-add-${this.id}" class="objective-task-button fa-solid fa-plus fa-2x" title="Add Task"></div>
|
<div id="objective-task-add-${this.id}" class="objective-task-button fa-solid fa-plus fa-2x" title="Add Task"></div>
|
||||||
|
<div id="objective-task-add-branch-${this.id}" class="objective-task-button fa-solid fa-code-fork fa-2x" title="Branch Task"></div>
|
||||||
</div><br>
|
</div><br>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -221,6 +288,15 @@ class ObjectiveTask {
|
||||||
this.descriptionSpan = $(`#objective-task-description-${this.id}`);
|
this.descriptionSpan = $(`#objective-task-description-${this.id}`);
|
||||||
this.addButton = $(`#objective-task-add-${this.id}`);
|
this.addButton = $(`#objective-task-add-${this.id}`);
|
||||||
this.deleteButton = $(`#objective-task-delete-${this.id}`);
|
this.deleteButton = $(`#objective-task-delete-${this.id}`);
|
||||||
|
this.taskHtml = $(`#objective-task-label-${this.id}`);
|
||||||
|
this.branchButton = $(`#objective-task-add-branch-${this.id}`)
|
||||||
|
|
||||||
|
// Handle sub-task forking style
|
||||||
|
if (this.children.length > 0){
|
||||||
|
this.branchButton.css({'color':'#33cc33'})
|
||||||
|
} else {
|
||||||
|
this.branchButton.css({'color':''})
|
||||||
|
}
|
||||||
|
|
||||||
// Add event listeners and set properties
|
// Add event listeners and set properties
|
||||||
$(`#objective-task-complete-${this.id}`).prop('checked', this.completed);
|
$(`#objective-task-complete-${this.id}`).prop('checked', this.completed);
|
||||||
|
@ -229,151 +305,70 @@ class ObjectiveTask {
|
||||||
$(`#objective-task-description-${this.id}`).on('focusout', () => (this.onDescriptionFocusout()));
|
$(`#objective-task-description-${this.id}`).on('focusout', () => (this.onDescriptionFocusout()));
|
||||||
$(`#objective-task-delete-${this.id}`).on('click', () => (this.onDeleteClick()));
|
$(`#objective-task-delete-${this.id}`).on('click', () => (this.onDeleteClick()));
|
||||||
$(`#objective-task-add-${this.id}`).on('click', () => (this.onAddClick()));
|
$(`#objective-task-add-${this.id}`).on('click', () => (this.onAddClick()));
|
||||||
|
this.branchButton.on('click', () => (this.onBranchClick()))
|
||||||
|
}
|
||||||
|
|
||||||
|
onBranchClick() {
|
||||||
|
currentObjective = this
|
||||||
|
updateUiTaskList();
|
||||||
|
setCurrentTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
onCompleteClick(){
|
onCompleteClick(){
|
||||||
this.completed = this.completedCheckbox.prop('checked')
|
this.completed = this.completedCheckbox.prop('checked')
|
||||||
|
this.checkParentComplete()
|
||||||
setCurrentTask();
|
setCurrentTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDescriptionUpdate(){
|
onDescriptionUpdate(){
|
||||||
this.description = this.descriptionSpan.text();
|
this.description = this.descriptionSpan.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDescriptionFocusout(){
|
onDescriptionFocusout(){
|
||||||
setCurrentTask();
|
setCurrentTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteClick(){
|
onDeleteClick(){
|
||||||
deleteTask(this.id);
|
const index = this.getIndex()
|
||||||
|
const parent = getTaskById(this.parentId)
|
||||||
|
parent.children.splice(index, 1)
|
||||||
|
updateUiTaskList()
|
||||||
|
setCurrentTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddClick(){
|
onAddClick(){
|
||||||
const {_, index} = getTaskById(this.id)
|
const index = this.getIndex()
|
||||||
addTask("", index + 1);
|
const parent = getTaskById(this.parentId)
|
||||||
setCurrentTask();
|
parent.addTask("", index + 1);
|
||||||
updateUiTaskList();
|
updateUiTaskList();
|
||||||
|
setCurrentTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
toSaveState() {
|
toSaveStateRecurse() {
|
||||||
|
let children = []
|
||||||
|
if (this.children.length > 0){
|
||||||
|
for (const child of this.children){
|
||||||
|
children.push(child.toSaveStateRecurse())
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
"id":this.id,
|
"id":this.id,
|
||||||
"description":this.description,
|
"description":this.description,
|
||||||
"completed":this.completed,
|
"completed":this.completed,
|
||||||
"parent": this.parent,
|
"parentId": this.parentId,
|
||||||
|
"children": children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//###############################//
|
//###############################//
|
||||||
//# UI AND Settings #//
|
//# Custom Prompts #//
|
||||||
//###############################//
|
//###############################//
|
||||||
|
|
||||||
|
|
||||||
const defaultSettings = {
|
|
||||||
objective: "",
|
|
||||||
tasks: [],
|
|
||||||
chatDepth: 2,
|
|
||||||
checkFrequency: 3,
|
|
||||||
hideTasks: false,
|
|
||||||
prompts: defaultPrompts,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenient single call. Not much at the moment.
|
|
||||||
function resetState() {
|
|
||||||
loadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
function saveState() {
|
|
||||||
const context = getContext();
|
|
||||||
|
|
||||||
if (currentChatId == "") {
|
|
||||||
currentChatId = context.chatId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert globalTasks for saving
|
|
||||||
const tasks = globalTasks.map(task => {return task.toSaveState()})
|
|
||||||
|
|
||||||
chat_metadata['objective'] = {
|
|
||||||
objective: globalObjective,
|
|
||||||
tasks: tasks,
|
|
||||||
checkFrequency: $('#objective-check-frequency').val(),
|
|
||||||
chatDepth: $('#objective-chat-depth').val(),
|
|
||||||
hideTasks: $('#objective-hide-tasks').prop('checked'),
|
|
||||||
prompts: objectivePrompts,
|
|
||||||
}
|
|
||||||
|
|
||||||
saveMetadataDebounced();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump core state
|
|
||||||
function debugObjectiveExtension() {
|
|
||||||
console.log(JSON.stringify({
|
|
||||||
"currentTask": currentTask.description,
|
|
||||||
"globalObjective": globalObjective,
|
|
||||||
"globalTasks": globalTasks.map(v => {return v.toSaveState()}),
|
|
||||||
"chat_metadata": chat_metadata['objective'],
|
|
||||||
"extension_settings": extension_settings['objective'],
|
|
||||||
"prompts": objectivePrompts
|
|
||||||
}, null, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
window.debugObjectiveExtension = debugObjectiveExtension
|
|
||||||
|
|
||||||
|
|
||||||
// Populate UI task list
|
|
||||||
function updateUiTaskList() {
|
|
||||||
$('#objective-tasks').empty()
|
|
||||||
// Show tasks if there are any
|
|
||||||
if (globalTasks.length > 0){
|
|
||||||
for (const task of globalTasks) {
|
|
||||||
task.addUiElement()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Show button to add tasks if there are none
|
|
||||||
$('#objective-tasks').append(`
|
|
||||||
<input id="objective-task-add-first" type="button" class="menu_button" value="Add Task">
|
|
||||||
`)
|
|
||||||
$("#objective-task-add-first").on('click', () => {
|
|
||||||
addTask("")
|
|
||||||
setCurrentTask()
|
|
||||||
updateUiTaskList()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Trigger creation of new tasks with given objective.
|
|
||||||
async function onGenerateObjectiveClick() {
|
|
||||||
globalObjective = $('#objective-text').val()
|
|
||||||
await generateTasks()
|
|
||||||
saveState()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update extension prompts
|
|
||||||
function onChatDepthInput() {
|
|
||||||
saveState()
|
|
||||||
setCurrentTask() // Ensure extension prompt is updated
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update how often we check for task completion
|
|
||||||
function onCheckFrequencyInput() {
|
|
||||||
checkCounter = $("#objective-check-frequency").val()
|
|
||||||
$('#objective-counter').text(checkCounter)
|
|
||||||
saveState()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onHideTasksInput() {
|
|
||||||
$('#objective-tasks').prop('hidden', $('#objective-hide-tasks').prop('checked'))
|
|
||||||
saveState()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEditPromptClick() {
|
function onEditPromptClick() {
|
||||||
let popupText = ''
|
let popupText = ''
|
||||||
popupText += `
|
popupText += `
|
||||||
<div class="objective_prompt_modal">
|
<div class="objective_prompt_modal">
|
||||||
<div class="alignitemsflexstart flex-container">
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<label for="objective-prompt-generate">Generation Prompt</label>
|
<label for="objective-prompt-generate">Generation Prompt</label>
|
||||||
<textarea id="objective-prompt-generate" type="text" class="text_pole textarea_compact" rows="8"></textarea>
|
<textarea id="objective-prompt-generate" type="text" class="text_pole textarea_compact" rows="8"></textarea>
|
||||||
|
@ -382,12 +377,14 @@ function onEditPromptClick() {
|
||||||
<label for="objective-prompt-extension-prompt">Injected Prompt</label>
|
<label for="objective-prompt-extension-prompt">Injected Prompt</label>
|
||||||
<textarea id="objective-prompt-extension-prompt" type="text" class="text_pole textarea_compact" rows="8"></textarea>
|
<textarea id="objective-prompt-extension-prompt" type="text" class="text_pole textarea_compact" rows="8"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="alignitemsflexstart flex-container">
|
<div class="objective_prompt_block">
|
||||||
<input id="objective-custom-prompt-name" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" placeholder="Custom Prompt Name">
|
<input id="objective-custom-prompt-name" style="flex-grow:2" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" placeholder="Custom Prompt Name">
|
||||||
<input id="objective-custom-prompt-save" class="menu_button" type="submit" value="Save Custom Prompt" />
|
<input id="objective-custom-prompt-save" style="flex-grow:1" class="menu_button" type="submit" value="Save Prompt" />
|
||||||
<label for="objective-prompt-load"> Load Prompt </label>
|
</div>
|
||||||
|
<div class="objective_prompt_block">
|
||||||
|
<label for="objective-prompt-load">Load Prompt</label>
|
||||||
<select id="objective-prompt-load"><select>
|
<select id="objective-prompt-load"><select>
|
||||||
<input id="objective-custom-prompt-delete" class="menu_button" type="submit" value="Delete Custom Prompt" />
|
<input id="objective-custom-prompt-delete" class="menu_button" type="submit" value="Delete Prompt" />
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
callPopup(popupText, 'text')
|
callPopup(popupText, 'text')
|
||||||
|
@ -411,7 +408,7 @@ function onEditPromptClick() {
|
||||||
|
|
||||||
// Handle save
|
// Handle save
|
||||||
$('#objective-custom-prompt-save').on('click', () => {
|
$('#objective-custom-prompt-save').on('click', () => {
|
||||||
addCustomPrompt($('#objective-custom-prompt-name').val(), objectivePrompts)
|
saveCustomPrompt($('#objective-custom-prompt-name').val(), objectivePrompts)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle delete
|
// Handle delete
|
||||||
|
@ -424,7 +421,7 @@ function onEditPromptClick() {
|
||||||
$('#objective-prompt-load').on('change', loadCustomPrompt)
|
$('#objective-prompt-load').on('change', loadCustomPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCustomPrompt(customPromptName, customPrompts) {
|
function saveCustomPrompt(customPromptName, customPrompts) {
|
||||||
if (customPromptName == "") {
|
if (customPromptName == "") {
|
||||||
toastr.warning("Please set custom prompt name to save.")
|
toastr.warning("Please set custom prompt name to save.")
|
||||||
return
|
return
|
||||||
|
@ -453,7 +450,7 @@ function deleteCustomPrompt(customPromptName){
|
||||||
function loadCustomPrompt(){
|
function loadCustomPrompt(){
|
||||||
const optionSelected = $("#objective-prompt-load").find(':selected').val()
|
const optionSelected = $("#objective-prompt-load").find(':selected').val()
|
||||||
console.log(optionSelected)
|
console.log(optionSelected)
|
||||||
objectivePrompts = extension_settings.objective.customPrompts[optionSelected]
|
Object.assign(objectivePrompts, extension_settings.objective.customPrompts[optionSelected])
|
||||||
|
|
||||||
$('#objective-prompt-generate').val(objectivePrompts.createTask)
|
$('#objective-prompt-generate').val(objectivePrompts.createTask)
|
||||||
$('#objective-prompt-check').val(objectivePrompts.checkTaskCompleted)
|
$('#objective-prompt-check').val(objectivePrompts.checkTaskCompleted)
|
||||||
|
@ -472,10 +469,149 @@ function populateCustomPrompts(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//###############################//
|
||||||
|
//# UI AND Settings #//
|
||||||
|
//###############################//
|
||||||
|
|
||||||
|
|
||||||
|
const defaultSettings = {
|
||||||
|
currentObjectiveId: null,
|
||||||
|
taskTree: null,
|
||||||
|
chatDepth: 2,
|
||||||
|
checkFrequency: 3,
|
||||||
|
hideTasks: false,
|
||||||
|
prompts: defaultPrompts,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenient single call. Not much at the moment.
|
||||||
|
function resetState() {
|
||||||
|
loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
function saveState() {
|
||||||
|
const context = getContext();
|
||||||
|
|
||||||
|
if (currentChatId == "") {
|
||||||
|
currentChatId = context.chatId
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_metadata['objective'] = {
|
||||||
|
currentObjectiveId: currentObjective.id,
|
||||||
|
taskTree: taskTree.toSaveStateRecurse(),
|
||||||
|
checkFrequency: $('#objective-check-frequency').val(),
|
||||||
|
chatDepth: $('#objective-chat-depth').val(),
|
||||||
|
hideTasks: $('#objective-hide-tasks').prop('checked'),
|
||||||
|
prompts: objectivePrompts,
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMetadataDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump core state
|
||||||
|
function debugObjectiveExtension() {
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
"currentTask": currentTask,
|
||||||
|
"currentObjective": currentObjective,
|
||||||
|
"taskTree": taskTree.toSaveStateRecurse(),
|
||||||
|
"chat_metadata": chat_metadata['objective'],
|
||||||
|
"extension_settings": extension_settings['objective'],
|
||||||
|
"prompts": objectivePrompts
|
||||||
|
}, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
window.debugObjectiveExtension = debugObjectiveExtension
|
||||||
|
|
||||||
|
|
||||||
|
// Populate UI task list
|
||||||
|
function updateUiTaskList() {
|
||||||
|
$('#objective-tasks').empty()
|
||||||
|
|
||||||
|
// Show button to navigate back to parent objective if parent exists
|
||||||
|
if (currentObjective){
|
||||||
|
if (currentObjective.parentId !== "") {
|
||||||
|
$('#objective-parent').show()
|
||||||
|
} else {
|
||||||
|
$('#objective-parent').hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#objective-text').val(currentObjective.description)
|
||||||
|
if (currentObjective.children.length > 0){
|
||||||
|
// Show tasks if there are any to show
|
||||||
|
for (const task of currentObjective.children) {
|
||||||
|
task.addUiElement()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show button to add tasks if there are none
|
||||||
|
$('#objective-tasks').append(`
|
||||||
|
<input id="objective-task-add-first" type="button" class="menu_button" value="Add Task">
|
||||||
|
`)
|
||||||
|
$("#objective-task-add-first").on('click', () => {
|
||||||
|
currentObjective.addTask("")
|
||||||
|
setCurrentTask()
|
||||||
|
updateUiTaskList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onParentClick() {
|
||||||
|
currentObjective = getTaskById(currentObjective.parentId)
|
||||||
|
updateUiTaskList()
|
||||||
|
setCurrentTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger creation of new tasks with given objective.
|
||||||
|
async function onGenerateObjectiveClick() {
|
||||||
|
await generateTasks()
|
||||||
|
saveState()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update extension prompts
|
||||||
|
function onChatDepthInput() {
|
||||||
|
saveState()
|
||||||
|
setCurrentTask() // Ensure extension prompt is updated
|
||||||
|
}
|
||||||
|
|
||||||
|
function onObjectiveTextFocusOut(){
|
||||||
|
currentObjective.description = $('#objective-text').val()
|
||||||
|
saveState()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update how often we check for task completion
|
||||||
|
function onCheckFrequencyInput() {
|
||||||
|
checkCounter = $("#objective-check-frequency").val()
|
||||||
|
$('#objective-counter').text(checkCounter)
|
||||||
|
saveState()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHideTasksInput() {
|
||||||
|
$('#objective-tasks').prop('hidden', $('#objective-hide-tasks').prop('checked'))
|
||||||
|
saveState()
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTaskChildrenRecurse(savedTask) {
|
||||||
|
let tempTaskTree = new ObjectiveTask({
|
||||||
|
id: savedTask.id,
|
||||||
|
description: savedTask.description,
|
||||||
|
completed: savedTask.completed,
|
||||||
|
parentId: savedTask.parentId,
|
||||||
|
})
|
||||||
|
for (const task of savedTask.children){
|
||||||
|
const childTask = loadTaskChildrenRecurse(task)
|
||||||
|
tempTaskTree.children.push(childTask)
|
||||||
|
}
|
||||||
|
return tempTaskTree
|
||||||
|
}
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
// Load/Init settings for chatId
|
// Load/Init settings for chatId
|
||||||
currentChatId = getContext().chatId
|
currentChatId = getContext().chatId
|
||||||
|
|
||||||
|
// Reset Objectives and Tasks in memory
|
||||||
|
taskTree = null;
|
||||||
|
currentObjective = null;
|
||||||
|
|
||||||
// Init extension settings
|
// Init extension settings
|
||||||
if (Object.keys(extension_settings.objective).length === 0) {
|
if (Object.keys(extension_settings.objective).length === 0) {
|
||||||
Object.assign(extension_settings.objective, { 'customPrompts': {'default':defaultPrompts}})
|
Object.assign(extension_settings.objective, { 'customPrompts': {'default':defaultPrompts}})
|
||||||
|
@ -488,6 +624,7 @@ function loadSettings() {
|
||||||
|
|
||||||
// Migrate existing settings
|
// Migrate existing settings
|
||||||
if (currentChatId in extension_settings.objective) {
|
if (currentChatId in extension_settings.objective) {
|
||||||
|
// TODO: Remove this soon
|
||||||
chat_metadata['objective'] = extension_settings.objective[currentChatId];
|
chat_metadata['objective'] = extension_settings.objective[currentChatId];
|
||||||
delete extension_settings.objective[currentChatId];
|
delete extension_settings.objective[currentChatId];
|
||||||
}
|
}
|
||||||
|
@ -496,21 +633,47 @@ function loadSettings() {
|
||||||
Object.assign(chat_metadata, { objective: defaultSettings });
|
Object.assign(chat_metadata, { objective: defaultSettings });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update globals
|
// Migrate legacy flat objective to new objectiveTree and currentObjective
|
||||||
globalObjective = chat_metadata['objective'].objective
|
if ('objective' in chat_metadata.objective) {
|
||||||
globalTasks = chat_metadata['objective'].tasks.map(task => {
|
|
||||||
return new ObjectiveTask({
|
// Create root objective from legacy objective
|
||||||
id: task.id,
|
taskTree = new ObjectiveTask({id:0, description: chat_metadata.objective.objective});
|
||||||
description: task.description,
|
currentObjective = taskTree;
|
||||||
completed: task.completed,
|
|
||||||
parent: task.parent,
|
// Populate root objective tree from legacy tasks
|
||||||
})
|
if ('tasks' in chat_metadata.objective) {
|
||||||
});
|
let idIncrement = 0;
|
||||||
|
taskTree.children = chat_metadata.objective.tasks.map(task => {
|
||||||
|
idIncrement += 1;
|
||||||
|
return new ObjectiveTask({
|
||||||
|
id: idIncrement,
|
||||||
|
description: task.description,
|
||||||
|
completed: task.completed,
|
||||||
|
parentId: taskTree.id,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
saveState();
|
||||||
|
delete chat_metadata.objective.objective;
|
||||||
|
delete chat_metadata.objective.tasks;
|
||||||
|
} else {
|
||||||
|
// Load Objectives and Tasks (Normal path)
|
||||||
|
if (chat_metadata.objective.taskTree){
|
||||||
|
taskTree = loadTaskChildrenRecurse(chat_metadata.objective.taskTree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there's a root task
|
||||||
|
if (!taskTree) {
|
||||||
|
taskTree = new ObjectiveTask({id:0,description:$('#objective-text').val()})
|
||||||
|
}
|
||||||
|
|
||||||
|
currentObjective = taskTree
|
||||||
checkCounter = chat_metadata['objective'].checkFrequency
|
checkCounter = chat_metadata['objective'].checkFrequency
|
||||||
|
|
||||||
// Update UI elements
|
// Update UI elements
|
||||||
$('#objective-counter').text(checkCounter)
|
$('#objective-counter').text(checkCounter)
|
||||||
$("#objective-text").text(globalObjective)
|
$("#objective-text").text(taskTree.description)
|
||||||
updateUiTaskList()
|
updateUiTaskList()
|
||||||
$('#objective-chat-depth').val(chat_metadata['objective'].chatDepth)
|
$('#objective-chat-depth').val(chat_metadata['objective'].chatDepth)
|
||||||
$('#objective-check-frequency').val(chat_metadata['objective'].checkFrequency)
|
$('#objective-check-frequency').val(chat_metadata['objective'].checkFrequency)
|
||||||
|
@ -533,38 +696,44 @@ jQuery(() => {
|
||||||
<div class="objective-settings">
|
<div class="objective-settings">
|
||||||
<div class="inline-drawer">
|
<div class="inline-drawer">
|
||||||
<div class="inline-drawer-toggle inline-drawer-header">
|
<div class="inline-drawer-toggle inline-drawer-header">
|
||||||
<b>Objective</b>
|
<b>Objective</b>
|
||||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
</div>
|
|
||||||
<div class="inline-drawer-content">
|
|
||||||
<label for="objective-text"><small>Enter an objective and generate tasks. The AI will attempt to complete tasks autonomously</small></label>
|
|
||||||
<textarea id="objective-text" type="text" class="text_pole textarea_compact" rows="4"></textarea>
|
|
||||||
<div class="objective_block flex-container">
|
|
||||||
<input id="objective-generate" class="menu_button" type="submit" value="Auto-Generate Tasks" />
|
|
||||||
<label class="checkbox_label"><input id="objective-hide-tasks" type="checkbox"> Hide Tasks</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="inline-drawer-content">
|
||||||
<div id="objective-tasks"> </div>
|
<label for="objective-text"><small>Enter an objective and generate tasks. The AI will attempt to complete tasks autonomously</small></label>
|
||||||
<div class="objective_block margin-bot-10px">
|
<textarea id="objective-text" type="text" class="text_pole textarea_compact" rows="4"></textarea>
|
||||||
<div class="objective_block objective_block_control flex1 flexFlowColumn">
|
<div class="objective_block flex-container">
|
||||||
<label for="objective-chat-depth">Position in Chat</label>
|
<input id="objective-generate" class="menu_button" type="submit" value="Auto-Generate Tasks" />
|
||||||
<input id="objective-chat-depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
<label class="checkbox_label"><input id="objective-hide-tasks" type="checkbox"> Hide Tasks</label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<div id="objective-parent" class="objective_block flex-container">
|
||||||
<div class="objective_block objective_block_control flex1">
|
<i class="objective-task-button fa-solid fa-circle-left fa-2x" title="Go to Parent"></i>
|
||||||
|
<small>Go to parent task</small>
|
||||||
<label for="objective-check-frequency">Task Check Frequency</label>
|
|
||||||
<input id="objective-check-frequency" class="text_pole widthUnset" type="number" min="0" max="99" />
|
|
||||||
<small>(0 = disabled)</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="objective-tasks"> </div>
|
||||||
|
<div class="objective_block margin-bot-10px">
|
||||||
|
<div class="objective_block objective_block_control flex1 flexFlowColumn">
|
||||||
|
<label for="objective-chat-depth">Position in Chat</label>
|
||||||
|
<input id="objective-chat-depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="objective_block objective_block_control flex1">
|
||||||
|
|
||||||
|
<label for="objective-check-frequency">Task Check Frequency</label>
|
||||||
|
<input id="objective-check-frequency" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||||
|
<small>(0 = disabled)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span> Messages until next AI task completion check <span id="objective-counter">0</span></span>
|
||||||
|
<div class="objective_block flex-container">
|
||||||
|
<input id="objective_prompt_edit" class="menu_button" type="submit" value="Edit Prompts" />
|
||||||
|
</div>
|
||||||
|
<hr class="sysHR">
|
||||||
</div>
|
</div>
|
||||||
<span> Messages until next AI task completion check <span id="objective-counter">0</span></span>
|
|
||||||
<div class="objective_block flex-container">
|
|
||||||
<input id="objective_prompt_edit" class="menu_button" type="submit" value="Edit Prompts" />
|
|
||||||
</div>
|
|
||||||
<hr class="sysHR">
|
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
addManualTaskCheckUi()
|
addManualTaskCheckUi()
|
||||||
$('#extensions_settings').append(settingsHtml);
|
$('#extensions_settings').append(settingsHtml);
|
||||||
|
@ -573,6 +742,9 @@ jQuery(() => {
|
||||||
$("#objective-check-frequency").on('input', onCheckFrequencyInput)
|
$("#objective-check-frequency").on('input', onCheckFrequencyInput)
|
||||||
$('#objective-hide-tasks').on('click', onHideTasksInput)
|
$('#objective-hide-tasks').on('click', onHideTasksInput)
|
||||||
$('#objective_prompt_edit').on('click', onEditPromptClick)
|
$('#objective_prompt_edit').on('click', onEditPromptClick)
|
||||||
|
$('#objective-parent').hide()
|
||||||
|
$('#objective-parent').on('click',onParentClick)
|
||||||
|
$('#objective-text').on('focusout',onObjectiveTextFocusOut)
|
||||||
loadSettings()
|
loadSettings()
|
||||||
|
|
||||||
eventSource.on(event_types.CHAT_CHANGED, () => {
|
eventSource.on(event_types.CHAT_CHANGED, () => {
|
||||||
|
|
|
@ -10,6 +10,13 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.objective_prompt_block {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
column-gap: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.objective_block_control {
|
.objective_block_control {
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,48 @@
|
||||||
import { saveSettingsDebounced } from "../../../script.js";
|
import { saveSettingsDebounced, callPopup, getRequestHeaders } from "../../../script.js";
|
||||||
import { getContext, extension_settings } from "../../extensions.js";
|
import { getContext, extension_settings } from "../../extensions.js";
|
||||||
import { initScrollHeight, resetScrollHeight } from "../../utils.js";
|
import { initScrollHeight, resetScrollHeight } from "../../utils.js";
|
||||||
|
|
||||||
|
|
||||||
export { MODULE_NAME };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = 'quick-reply';
|
const MODULE_NAME = 'quick-reply';
|
||||||
const UPDATE_INTERVAL = 1000;
|
const UPDATE_INTERVAL = 1000;
|
||||||
|
let presets = [];
|
||||||
|
let selected_preset = '';
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
quickReplyEnabled: false,
|
quickReplyEnabled: true,
|
||||||
numberOfSlots: 5,
|
numberOfSlots: 5,
|
||||||
quickReplySlots: [],
|
quickReplySlots: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSettings() {
|
//method from worldinfo
|
||||||
|
async function updateQuickReplyPresetList() {
|
||||||
|
var result = await fetch("/getsettings", {
|
||||||
|
method: "POST",
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.ok) {
|
||||||
|
var data = await result.json();
|
||||||
|
presets = data.quickReplyPresets?.length ? data.quickReplyPresets : [];
|
||||||
|
console.log(presets)
|
||||||
|
$("#quickReplyPresets").find('option[value!=""]').remove();
|
||||||
|
|
||||||
|
|
||||||
|
if (presets !== undefined) {
|
||||||
|
presets.forEach((item, i) => {
|
||||||
|
$("#quickReplyPresets").append(`<option value='${item.name}'${selected_preset.includes(item.name) ? ' selected' : ''}>${item.name}</option>`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSettings(type) {
|
||||||
|
if (type === 'init') {
|
||||||
|
await updateQuickReplyPresetList()
|
||||||
|
}
|
||||||
if (Object.keys(extension_settings.quickReply).length === 0) {
|
if (Object.keys(extension_settings.quickReply).length === 0) {
|
||||||
Object.assign(extension_settings.quickReply, defaultSettings);
|
Object.assign(extension_settings.quickReply, defaultSettings);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +140,51 @@ async function moduleWorker() {
|
||||||
if (extension_settings.quickReply.quickReplyEnabled === true) {
|
if (extension_settings.quickReply.quickReplyEnabled === true) {
|
||||||
$('#quickReplyBar').toggle(getContext().onlineStatus !== 'no_connection');
|
$('#quickReplyBar').toggle(getContext().onlineStatus !== 'no_connection');
|
||||||
}
|
}
|
||||||
|
if (extension_settings.quickReply.selectedPreset) {
|
||||||
|
selected_preset = extension_settings.quickReply.selectedPreset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveQuickReplyPreset() {
|
||||||
|
const name = await callPopup('Enter a name for the Quick Reply Preset:', 'input');
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quickReplyPreset = {
|
||||||
|
name: name,
|
||||||
|
quickReplyEnabled: extension_settings.quickReply.quickReplyEnabled,
|
||||||
|
quickReplySlots: extension_settings.quickReply.quickReplySlots,
|
||||||
|
numberOfSlots: extension_settings.quickReply.numberOfSlots,
|
||||||
|
selectedPreset: name
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/savequickreply', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify(quickReplyPreset)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const quickReplyPresetIndex = presets.findIndex(x => x.name == name);
|
||||||
|
|
||||||
|
if (quickReplyPresetIndex == -1) {
|
||||||
|
presets.push(quickReplyPreset);
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.selected = true;
|
||||||
|
option.value = name;
|
||||||
|
option.innerText = name;
|
||||||
|
$('#quickReplyPresets').append(option);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
presets[quickReplyPresetIndex] = quickReplyPreset;
|
||||||
|
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||||
|
}
|
||||||
|
saveSettingsDebounced();
|
||||||
|
} else {
|
||||||
|
toastr.warning('Failed to save Quick Reply Preset.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onQuickReplyNumberOfSlotsInput() {
|
async function onQuickReplyNumberOfSlotsInput() {
|
||||||
|
@ -178,6 +252,27 @@ function generateQuickReplyElements() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function applyQuickReplyPreset(name) {
|
||||||
|
const quickReplyPreset = presets.find(x => x.name == name);
|
||||||
|
|
||||||
|
if (!quickReplyPreset) {
|
||||||
|
console.log(`error, QR preset '${name}' not found`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_settings.quickReply = quickReplyPreset;
|
||||||
|
extension_settings.quickReply.selectedPreset = name;
|
||||||
|
saveSettingsDebounced()
|
||||||
|
loadSettings('init')
|
||||||
|
addQuickReplyBar();
|
||||||
|
moduleWorker();
|
||||||
|
|
||||||
|
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||||
|
|
||||||
|
console.debug('QR Preset applied: ' + name);
|
||||||
|
//loadMovingUIState()
|
||||||
|
}
|
||||||
|
|
||||||
jQuery(async () => {
|
jQuery(async () => {
|
||||||
|
|
||||||
moduleWorker();
|
moduleWorker();
|
||||||
|
@ -190,11 +285,18 @@ jQuery(async () => {
|
||||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<label class="checkbox_label marginBot10">
|
<div class="flex-container ">
|
||||||
<input id="quickReplyEnabled" type="checkbox" />
|
<label class="checkbox_label marginBot10 wide100p flexnowrap">
|
||||||
Enable Quick Replies
|
<input id="quickReplyEnabled" type="checkbox" />
|
||||||
</label>
|
Enable Quick Replies
|
||||||
<label for="quickReplyNumberOfSlots">Number of slots:</label>
|
</label>
|
||||||
|
<div class="flex-container flexnowrap wide100p">
|
||||||
|
<select id="quickReplyPresets" name="quickreply-preset">
|
||||||
|
</select>
|
||||||
|
<i id="quickReplyPresetSaveButton" class="fa-solid fa-save"></i>
|
||||||
|
</div>
|
||||||
|
<label for="quickReplyNumberOfSlots">Number of slots:</label>
|
||||||
|
</div>
|
||||||
<div class="flex-container flexGap5 flexnowrap">
|
<div class="flex-container flexGap5 flexnowrap">
|
||||||
<input id="quickReplyNumberOfSlots" class="text_pole" type="number" min="1" max="100" value="" />
|
<input id="quickReplyNumberOfSlots" class="text_pole" type="number" min="1" max="100" value="" />
|
||||||
<div class="menu_button menu_button_icon" id="quickReplyNumberOfSlotsApply">
|
<div class="menu_button menu_button_icon" id="quickReplyNumberOfSlotsApply">
|
||||||
|
@ -212,8 +314,17 @@ jQuery(async () => {
|
||||||
|
|
||||||
$('#quickReplyEnabled').on('input', onQuickReplyEnabledInput);
|
$('#quickReplyEnabled').on('input', onQuickReplyEnabledInput);
|
||||||
$('#quickReplyNumberOfSlotsApply').on('click', onQuickReplyNumberOfSlotsInput);
|
$('#quickReplyNumberOfSlotsApply').on('click', onQuickReplyNumberOfSlotsInput);
|
||||||
|
$("#quickReplyPresetSaveButton").on('click', saveQuickReplyPreset);
|
||||||
|
|
||||||
await loadSettings();
|
$("#quickReplyPresets").on('change', async function () {
|
||||||
|
const quickReplyPresetSelected = $(this).find(':selected').val();
|
||||||
|
extension_settings.quickReplyPreset = quickReplyPresetSelected;
|
||||||
|
applyQuickReplyPreset(quickReplyPresetSelected);
|
||||||
|
saveSettingsDebounced();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
await loadSettings('init');
|
||||||
addQuickReplyBar();
|
addQuickReplyBar();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
// Borrowed from Agnai (AGPLv3)
|
||||||
|
// https://github.com/agnaistic/agnai/blob/dev/web/pages/Chat/components/SpeechRecognitionRecorder.tsx
|
||||||
|
// First version by Cohee#1207
|
||||||
|
// Adapted by Tony-sama
|
||||||
|
|
||||||
|
export { BrowserSttProvider }
|
||||||
|
|
||||||
|
const DEBUG_PREFIX = "<Speech Recognition module (Browser)> "
|
||||||
|
|
||||||
|
class BrowserSttProvider {
|
||||||
|
//########//
|
||||||
|
// Config //
|
||||||
|
//########//
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
language: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultSettings = {
|
||||||
|
language: "en-US",
|
||||||
|
}
|
||||||
|
|
||||||
|
processTranscriptFunction = null;
|
||||||
|
|
||||||
|
get settingsHtml() {
|
||||||
|
let html = ' \
|
||||||
|
<span>Language</span> </br> \
|
||||||
|
<select id="speech_recognition_browser_provider_language"> \
|
||||||
|
<option value="ar-SA">ar-SA: Arabic (Saudi Arabia)</option> \
|
||||||
|
<option value="bn-BD">bn-BD: Bangla (Bangladesh)</option> \
|
||||||
|
<option value="bn-IN">bn-IN: Bangla (India)</option> \
|
||||||
|
<option value="cs-CZ">cs-CZ: Czech (Czech Republic)</option> \
|
||||||
|
<option value="da-DK">da-DK: Danish (Denmark)</option> \
|
||||||
|
<option value="de-AT">de-AT: German (Austria)</option> \
|
||||||
|
<option value="de-CH">de-CH: German (Switzerland)</option> \
|
||||||
|
<option value="de-DE">de-DE: German (Germany)</option> \
|
||||||
|
<option value="el-GR">el-GR: Greek (Greece)</option> \
|
||||||
|
<option value="en-AU">en-AU: English (Australia)</option> \
|
||||||
|
<option value="en-CA">en-CA: English (Canada)</option> \
|
||||||
|
<option value="en-GB">en-GB: English (United Kingdom)</option> \
|
||||||
|
<option value="en-IE">en-IE: English (Ireland)</option> \
|
||||||
|
<option value="en-IN">en-IN: English (India)</option> \
|
||||||
|
<option value="en-NZ">en-NZ: English (New Zealand)</option> \
|
||||||
|
<option value="en-US">en-US: English (United States)</option> \
|
||||||
|
<option value="en-ZA">en-ZA: English (South Africa)</option> \
|
||||||
|
<option value="es-AR">es-AR: Spanish (Argentina)</option> \
|
||||||
|
<option value="es-CL">es-CL: Spanish (Chile)</option> \
|
||||||
|
<option value="es-CO">es-CO: Spanish (Columbia)</option> \
|
||||||
|
<option value="es-ES">es-ES: Spanish (Spain)</option> \
|
||||||
|
<option value="es-MX">es-MX: Spanish (Mexico)</option> \
|
||||||
|
<option value="es-US">es-US: Spanish (United States)</option> \
|
||||||
|
<option value="fi-FI">fi-FI: Finnish (Finland)</option> \
|
||||||
|
<option value="fr-BE">fr-BE: French (Belgium)</option> \
|
||||||
|
<option value="fr-CA">fr-CA: French (Canada)</option> \
|
||||||
|
<option value="fr-CH">fr-CH: French (Switzerland)</option> \
|
||||||
|
<option value="fr-FR">fr-FR: French (France)</option> \
|
||||||
|
<option value="he-IL">he-IL: Hebrew (Israel)</option> \
|
||||||
|
<option value="hi-IN">hi-IN: Hindi (India)</option> \
|
||||||
|
<option value="hu-HU">hu-HU: Hungarian (Hungary)</option> \
|
||||||
|
<option value="id-ID">id-ID: Indonesian (Indonesia)</option> \
|
||||||
|
<option value="it-CH">it-CH: Italian (Switzerland)</option> \
|
||||||
|
<option value="it-IT">it-IT: Italian (Italy)</option> \
|
||||||
|
<option value="ja-JP">ja-JP: Japanese (Japan)</option> \
|
||||||
|
<option value="ko-KR">ko-KR: Korean (Republic of Korea)</option> \
|
||||||
|
<option value="nl-BE">nl-BE: Dutch (Belgium)</option> \
|
||||||
|
<option value="nl-NL">nl-NL: Dutch (The Netherlands)</option> \
|
||||||
|
<option value="no-NO">no-NO: Norwegian (Norway)</option> \
|
||||||
|
<option value="pl-PL">pl-PL: Polish (Poland)</option> \
|
||||||
|
<option value="pt-BR">pt-BR: Portugese (Brazil)</option> \
|
||||||
|
<option value="pt-PT">pt-PT: Portugese (Portugal)</option> \
|
||||||
|
<option value="ro-RO">ro-RO: Romanian (Romania)</option> \
|
||||||
|
<option value="ru-RU">ru-RU: Russian (Russian Federation)</option> \
|
||||||
|
<option value="sk-SK">sk-SK: Slovak (Slovakia)</option> \
|
||||||
|
<option value="sv-SE">sv-SE: Swedish (Sweden)</option> \
|
||||||
|
<option value="ta-IN">ta-IN: Tamil (India)</option> \
|
||||||
|
<option value="ta-LK">ta-LK: Tamil (Sri Lanka)</option> \
|
||||||
|
<option value="th-TH">th-TH: Thai (Thailand)</option> \
|
||||||
|
<option value="tr-TR">tr-TR: Turkish (Turkey)</option> \
|
||||||
|
<option value="zh-CN">zh-CN: Chinese (China)</option> \
|
||||||
|
<option value="zh-HK">zh-HK: Chinese (Hond Kong)</option> \
|
||||||
|
<option value="zh-TW">zh-TW: Chinese (Taiwan)</option> \
|
||||||
|
</select> \
|
||||||
|
'
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsChange() {
|
||||||
|
// Used when provider settings are updated from UI
|
||||||
|
this.settings.language = $("#speech_recognition_browser_provider_language").val();
|
||||||
|
console.debug(DEBUG_PREFIX+"Change language to",this.settings.language);
|
||||||
|
this.loadSettings(this.settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static capitalizeInterim(interimTranscript) {
|
||||||
|
let capitalizeIndex = -1;
|
||||||
|
if (interimTranscript.length > 2 && interimTranscript[0] === ' ') capitalizeIndex = 1;
|
||||||
|
else if (interimTranscript.length > 1) capitalizeIndex = 0;
|
||||||
|
if (capitalizeIndex > -1) {
|
||||||
|
const spacing = capitalizeIndex > 0 ? ' '.repeat(capitalizeIndex - 1) : '';
|
||||||
|
const capitalized = interimTranscript[capitalizeIndex].toLocaleUpperCase();
|
||||||
|
const rest = interimTranscript.substring(capitalizeIndex + 1);
|
||||||
|
interimTranscript = spacing + capitalized + rest;
|
||||||
|
}
|
||||||
|
return interimTranscript;
|
||||||
|
}
|
||||||
|
|
||||||
|
static composeValues(previous, interim) {
|
||||||
|
let spacing = '';
|
||||||
|
if (previous.endsWith('.')) spacing = ' ';
|
||||||
|
return previous + spacing + interim;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings(settings) {
|
||||||
|
const processTranscript = this.processTranscriptFunction;
|
||||||
|
|
||||||
|
// Populate Provider UI given input settings
|
||||||
|
if (Object.keys(settings).length == 0) {
|
||||||
|
console.debug(DEBUG_PREFIX+"Using default browser STT settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise as defaultSettings
|
||||||
|
this.settings = this.defaultSettings;
|
||||||
|
|
||||||
|
for (const key in settings){
|
||||||
|
if (key in this.settings){
|
||||||
|
this.settings[key] = settings[key]
|
||||||
|
} else {
|
||||||
|
throw `Invalid setting passed to Speech recogniton extension (browser): ${key}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#speech_recognition_browser_provider_language").val(this.settings.language);
|
||||||
|
|
||||||
|
const speechRecognitionSettings = $.extend({
|
||||||
|
grammar: '' // Custom grammar
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
const speechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||||
|
const speechRecognitionList = window.SpeechGrammarList || window.webkitSpeechGrammarList;
|
||||||
|
|
||||||
|
if (!speechRecognition) {
|
||||||
|
console.warn(DEBUG_PREFIX+'Speech recognition is not supported in this browser.');
|
||||||
|
$("#microphone_button").hide();
|
||||||
|
toastr.error("Speech recognition is not supported in this browser, use another browser or another provider of SillyTavern-extras Speech recognition extension.", "Speech recognition activation Failed (Browser)", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recognition = new speechRecognition();
|
||||||
|
|
||||||
|
if (speechRecognitionSettings.grammar && speechRecognitionList) {
|
||||||
|
speechRecognitionList.addFromString(speechRecognitionSettings.grammar, 1);
|
||||||
|
recognition.grammars = speechRecognitionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
recognition.continuous = true;
|
||||||
|
recognition.interimResults = true;
|
||||||
|
recognition.lang = this.settings.language;
|
||||||
|
|
||||||
|
const textarea = $('#send_textarea');
|
||||||
|
const button = $('#microphone_button');
|
||||||
|
|
||||||
|
let listening = false;
|
||||||
|
button.off('click').on("click", function () {
|
||||||
|
if (listening) {
|
||||||
|
recognition.stop();
|
||||||
|
} else {
|
||||||
|
recognition.start();
|
||||||
|
}
|
||||||
|
listening = !listening;
|
||||||
|
});
|
||||||
|
|
||||||
|
let initialText = '';
|
||||||
|
|
||||||
|
recognition.onresult = function (speechEvent) {
|
||||||
|
let finalTranscript = '';
|
||||||
|
let interimTranscript = ''
|
||||||
|
|
||||||
|
for (let i = speechEvent.resultIndex; i < speechEvent.results.length; ++i) {
|
||||||
|
const transcript = speechEvent.results[i][0].transcript;
|
||||||
|
|
||||||
|
if (speechEvent.results[i].isFinal) {
|
||||||
|
let interim = BrowserSttProvider.capitalizeInterim(transcript);
|
||||||
|
if (interim != '') {
|
||||||
|
let final = finalTranscript;
|
||||||
|
final = BrowserSttProvider.composeValues(final, interim);
|
||||||
|
if (final.slice(-1) != '.' & final.slice(-1) != '?') final += '.';
|
||||||
|
finalTranscript = final;
|
||||||
|
recognition.abort();
|
||||||
|
listening = false;
|
||||||
|
}
|
||||||
|
interimTranscript = ' ';
|
||||||
|
} else {
|
||||||
|
interimTranscript += transcript;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interimTranscript = BrowserSttProvider.capitalizeInterim(interimTranscript);
|
||||||
|
|
||||||
|
textarea.val(initialText + finalTranscript + interimTranscript);
|
||||||
|
};
|
||||||
|
|
||||||
|
recognition.onerror = function (event) {
|
||||||
|
console.error('Error occurred in recognition:', event.error);
|
||||||
|
//if ($('#speech_recognition_debug').is(':checked'))
|
||||||
|
// toastr.error('Error occurred in recognition:'+ event.error, 'STT Generation error (Browser)', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
recognition.onend = function () {
|
||||||
|
listening = false;
|
||||||
|
button.toggleClass('fa-microphone fa-microphone-slash');
|
||||||
|
const newText = textarea.val().substring(initialText.length);
|
||||||
|
textarea.val(textarea.val().substring(0,initialText.length));
|
||||||
|
processTranscript(newText);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
recognition.onstart = function () {
|
||||||
|
initialText = textarea.val();
|
||||||
|
button.toggleClass('fa-microphone fa-microphone-slash');
|
||||||
|
|
||||||
|
if ($("#speech_recognition_message_mode").val() == "replace") {
|
||||||
|
textarea.val("");
|
||||||
|
initialText = ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$("#microphone_button").show();
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX+"Browser STT settings loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,110 +1,351 @@
|
||||||
// Borrowed from Agnai (AGPLv3)
|
/*
|
||||||
// https://github.com/agnaistic/agnai/blob/dev/web/pages/Chat/components/SpeechRecognitionRecorder.tsx
|
TODO:
|
||||||
function capitalizeInterim(interimTranscript) {
|
- try pseudo streaming audio by just sending chunk every X seconds and asking VOSK if it is full text.
|
||||||
let capitalizeIndex = -1;
|
*/
|
||||||
if (interimTranscript.length > 2 && interimTranscript[0] === ' ') capitalizeIndex = 1;
|
|
||||||
else if (interimTranscript.length > 1) capitalizeIndex = 0;
|
import { saveSettingsDebounced } from "../../../script.js";
|
||||||
if (capitalizeIndex > -1) {
|
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch } from "../../extensions.js";
|
||||||
const spacing = capitalizeIndex > 0 ? ' '.repeat(capitalizeIndex - 1) : '';
|
import { VoskSttProvider } from './vosk.js'
|
||||||
const capitalized = interimTranscript[capitalizeIndex].toLocaleUpperCase();
|
import { WhisperSttProvider } from './whisper.js'
|
||||||
const rest = interimTranscript.substring(capitalizeIndex + 1);
|
import { BrowserSttProvider } from './browser.js'
|
||||||
interimTranscript = spacing + capitalized + rest;
|
export { MODULE_NAME };
|
||||||
|
|
||||||
|
const MODULE_NAME = 'Speech Recognition';
|
||||||
|
const DEBUG_PREFIX = "<Speech Recognition module> "
|
||||||
|
|
||||||
|
let sttProviders = {
|
||||||
|
None: null,
|
||||||
|
Browser: BrowserSttProvider,
|
||||||
|
Whisper: WhisperSttProvider,
|
||||||
|
Vosk: VoskSttProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
let sttProvider = null
|
||||||
|
let sttProviderName = "None"
|
||||||
|
|
||||||
|
let audioRecording = false
|
||||||
|
const constraints = { audio: { sampleSize: 16, channelCount: 1, sampleRate: 16000 } };
|
||||||
|
let audioChunks = [];
|
||||||
|
|
||||||
|
async function processTranscript(transcript) {
|
||||||
|
try {
|
||||||
|
const transcriptOriginal = transcript;
|
||||||
|
let transcriptFormatted = transcriptOriginal.trim();
|
||||||
|
|
||||||
|
if (transcriptFormatted.length > 0)
|
||||||
|
{
|
||||||
|
console.debug(DEBUG_PREFIX+"recorded transcript: \""+transcriptFormatted+"\"");
|
||||||
|
const messageMode = extension_settings.speech_recognition.messageMode;
|
||||||
|
console.debug(DEBUG_PREFIX+"mode: "+messageMode);
|
||||||
|
|
||||||
|
let transcriptLower = transcriptFormatted.toLowerCase()
|
||||||
|
// remove punctuation
|
||||||
|
let transcriptRaw = transcriptLower.replace(/[^\w\s\']|_/g, "").replace(/\s+/g, " ");
|
||||||
|
|
||||||
|
// Check message mapping
|
||||||
|
if (extension_settings.speech_recognition.messageMappingEnabled) {
|
||||||
|
console.debug(DEBUG_PREFIX+"Start searching message mapping into:",transcriptRaw)
|
||||||
|
for (const key in extension_settings.speech_recognition.messageMapping) {
|
||||||
|
console.debug(DEBUG_PREFIX+"message mapping searching: ", key,"=>",extension_settings.speech_recognition.messageMapping[key]);
|
||||||
|
if (transcriptRaw.includes(key)) {
|
||||||
|
var message = extension_settings.speech_recognition.messageMapping[key];
|
||||||
|
console.debug(DEBUG_PREFIX+"message mapping found: ", key,"=>",extension_settings.speech_recognition.messageMapping[key]);
|
||||||
|
$("#send_textarea").val(message);
|
||||||
|
|
||||||
|
if (messageMode == "auto_send") await getContext().generate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX+"no message mapping found, processing transcript as normal message");
|
||||||
|
|
||||||
|
switch (messageMode) {
|
||||||
|
case "auto_send":
|
||||||
|
$('#send_textarea').val("") // clear message area to avoid double message
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX+"Sending message")
|
||||||
|
const context = getContext();
|
||||||
|
const messageText = transcriptFormatted;
|
||||||
|
const message = {
|
||||||
|
name: context.name1,
|
||||||
|
is_user: true,
|
||||||
|
is_name: true,
|
||||||
|
send_date: Date.now(),
|
||||||
|
mes: messageText,
|
||||||
|
};
|
||||||
|
context.chat.push(message);
|
||||||
|
context.addOneMessage(message);
|
||||||
|
|
||||||
|
await context.generate();
|
||||||
|
|
||||||
|
$('#debug_output').text("<SST-module DEBUG>: message sent: \""+ transcriptFormatted +"\"");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "replace":
|
||||||
|
console.debug(DEBUG_PREFIX+"Replacing message")
|
||||||
|
$('#send_textarea').val(transcriptFormatted);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "append":
|
||||||
|
console.debug(DEBUG_PREFIX+"Appending message")
|
||||||
|
$('#send_textarea').val($('#send_textarea').val()+" "+transcriptFormatted);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.debug(DEBUG_PREFIX+"Not supported stt message mode: "+messageMode)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.debug(DEBUG_PREFIX+"Empty transcript, do nothing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.debug(error);
|
||||||
}
|
}
|
||||||
return interimTranscript;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function composeValues(previous, interim) {
|
function loadNavigatorAudioRecording() {
|
||||||
let spacing = '';
|
if (navigator.mediaDevices.getUserMedia) {
|
||||||
if (previous.endsWith('.')) spacing = ' ';
|
console.debug(DEBUG_PREFIX+' getUserMedia supported by browser.');
|
||||||
return previous + spacing + interim;
|
|
||||||
|
let onSuccess = function(stream) {
|
||||||
|
const mediaRecorder = new MediaRecorder(stream);
|
||||||
|
|
||||||
|
$("#microphone_button").off('click').on("click", function() {
|
||||||
|
if (!audioRecording) {
|
||||||
|
mediaRecorder.start();
|
||||||
|
console.debug(mediaRecorder.state);
|
||||||
|
console.debug("recorder started");
|
||||||
|
audioRecording = true;
|
||||||
|
$("#microphone_button").toggleClass('fa-microphone fa-microphone-slash');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mediaRecorder.stop();
|
||||||
|
console.debug(mediaRecorder.state);
|
||||||
|
console.debug("recorder stopped");
|
||||||
|
audioRecording = false;
|
||||||
|
$("#microphone_button").toggleClass('fa-microphone fa-microphone-slash');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaRecorder.onstop = async function() {
|
||||||
|
console.debug(DEBUG_PREFIX+"data available after MediaRecorder.stop() called: ", audioChunks.length, " chunks");
|
||||||
|
const audioBlob = new Blob(audioChunks, { type: "audio/wav; codecs=0" });
|
||||||
|
audioChunks = [];
|
||||||
|
|
||||||
|
const transcript = await sttProvider.processAudio(audioBlob);
|
||||||
|
|
||||||
|
// TODO: lock and release recording while processing?
|
||||||
|
console.debug(DEBUG_PREFIX+"received transcript:", transcript);
|
||||||
|
processTranscript(transcript);
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaRecorder.ondataavailable = function(e) {
|
||||||
|
audioChunks.push(e.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let onError = function(err) {
|
||||||
|
console.debug(DEBUG_PREFIX+"The following error occured: " + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.debug(DEBUG_PREFIX+"getUserMedia not supported on your browser!");
|
||||||
|
toastr.error("getUserMedia not supported", DEBUG_PREFIX+"not supported for your browser.", { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(function ($) {
|
//##############//
|
||||||
$.fn.speechRecognitionPlugin = function (options) {
|
// STT Provider //
|
||||||
const settings = $.extend({
|
//##############//
|
||||||
grammar: '' // Custom grammar
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
const speechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
function loadSttProvider(provider) {
|
||||||
const speechRecognitionList = window.SpeechGrammarList || window.webkitSpeechGrammarList;
|
//Clear the current config and add new config
|
||||||
|
$("#speech_recognition_provider_settings").html("");
|
||||||
|
|
||||||
if (!speechRecognition) {
|
// Init provider references
|
||||||
console.warn('Speech recognition is not supported in this browser.');
|
extension_settings.speech_recognition.currentProvider = provider;
|
||||||
return;
|
sttProviderName = provider;
|
||||||
|
|
||||||
|
if (!(sttProviderName in extension_settings.speech_recognition)) {
|
||||||
|
console.warn(`Provider ${sttProviderName} not in Extension Settings, initiatilizing provider in settings`);
|
||||||
|
extension_settings.speech_recognition[sttProviderName] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#speech_recognition_provider').val(sttProviderName);
|
||||||
|
|
||||||
|
if (sttProviderName == "None") {
|
||||||
|
$("#microphone_button").hide();
|
||||||
|
$("#speech_recognition_message_mode_div").hide();
|
||||||
|
$("#speech_recognition_message_mapping_div").hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#speech_recognition_message_mode_div").show();
|
||||||
|
$("#speech_recognition_message_mapping_div").show();
|
||||||
|
|
||||||
|
sttProvider = new sttProviders[sttProviderName]
|
||||||
|
|
||||||
|
// Init provider settings
|
||||||
|
$('#speech_recognition_provider_settings').append(sttProvider.settingsHtml);
|
||||||
|
|
||||||
|
// Use microphone button as push to talk
|
||||||
|
if (sttProviderName == "Browser") {
|
||||||
|
sttProvider.processTranscriptFunction = processTranscript;
|
||||||
|
sttProvider.loadSettings(extension_settings.speech_recognition[sttProviderName]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sttProvider.loadSettings(extension_settings.speech_recognition[sttProviderName]);
|
||||||
|
loadNavigatorAudioRecording();
|
||||||
|
|
||||||
|
$("#microphone_button").show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSttProviderChange() {
|
||||||
|
const sttProviderSelection = $('#speech_recognition_provider').val();
|
||||||
|
loadSttProvider(sttProviderSelection);
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSttProviderSettingsInput() {
|
||||||
|
sttProvider.onSettingsChange();
|
||||||
|
|
||||||
|
// Persist changes to SillyTavern stt extension settings
|
||||||
|
extension_settings.speech_recognition[sttProviderName] = sttProvider.settings;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
console.info(`Saved settings ${sttProviderName} ${JSON.stringify(sttProvider.settings)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#############################//
|
||||||
|
// Extension UI and Settings //
|
||||||
|
//#############################//
|
||||||
|
|
||||||
|
const defaultSettings = {
|
||||||
|
currentProvider: "None",
|
||||||
|
messageMode: "append",
|
||||||
|
messageMappingText: "",
|
||||||
|
messageMapping: [],
|
||||||
|
messageMappingEnabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
if (Object.keys(extension_settings.speech_recognition).length === 0) {
|
||||||
|
Object.assign(extension_settings.speech_recognition, defaultSettings)
|
||||||
|
}
|
||||||
|
$('#speech_recognition_enabled').prop('checked',extension_settings.speech_recognition.enabled);
|
||||||
|
$('#speech_recognition_message_mode').val(extension_settings.speech_recognition.messageMode);
|
||||||
|
|
||||||
|
if (extension_settings.speech_recognition.messageMappingText.length > 0) {
|
||||||
|
$('#speech_recognition_message_mapping').val(extension_settings.speech_recognition.messageMappingText);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#speech_recognition_message_mapping_enabled').prop('checked',extension_settings.speech_recognition.messageMappingEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onMessageModeChange() {
|
||||||
|
extension_settings.speech_recognition.messageMode = $('#speech_recognition_message_mode').val();
|
||||||
|
|
||||||
|
if(sttProviderName != "Browser" & extension_settings.speech_recognition.messageMode == "auto_send") {
|
||||||
|
$("#speech_recognition_wait_response_div").show()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#speech_recognition_wait_response_div").hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onMessageMappingChange() {
|
||||||
|
let array = $('#speech_recognition_message_mapping').val().split(",");
|
||||||
|
array = array.map(element => {return element.trim();});
|
||||||
|
array = array.filter((str) => str !== '');
|
||||||
|
extension_settings.speech_recognition.messageMapping = {};
|
||||||
|
for (const text of array) {
|
||||||
|
if (text.includes("=")) {
|
||||||
|
const pair = text.toLowerCase().split("=")
|
||||||
|
extension_settings.speech_recognition.messageMapping[pair[0].trim()] = pair[1].trim()
|
||||||
|
console.debug(DEBUG_PREFIX+"Added mapping", pair[0],"=>", extension_settings.speech_recognition.messageMapping[pair[0]]);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
const recognition = new speechRecognition();
|
console.debug(DEBUG_PREFIX+"Wrong syntax for message mapping, no '=' found in:", text);
|
||||||
|
|
||||||
if (settings.grammar && speechRecognitionList) {
|
|
||||||
speechRecognitionList.addFromString(settings.grammar, 1);
|
|
||||||
recognition.grammars = speechRecognitionList;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
recognition.continuous = true;
|
$("#speech_recognition_message_mapping_status").text("Message mapping updated to: "+JSON.stringify(extension_settings.speech_recognition.messageMapping))
|
||||||
recognition.interimResults = true;
|
console.debug(DEBUG_PREFIX+"Updated message mapping", extension_settings.speech_recognition.messageMapping);
|
||||||
// TODO: This should be configurable.
|
extension_settings.speech_recognition.messageMappingText = $('#speech_recognition_message_mapping').val()
|
||||||
recognition.lang = 'en-US'; // Set the language to English (US).
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
const $textarea = this;
|
async function onMessageMappingEnabledClick() {
|
||||||
const $button = $('<div class="fa-solid fa-microphone speech-toggle" title="Click to speak"></div>');
|
extension_settings.speech_recognition.messageMappingEnabled = $('#speech_recognition_message_mapping_enabled').is(':checked');
|
||||||
|
saveSettingsDebounced()
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
function addExtensionControls() {
|
||||||
|
const settingsHtml = `
|
||||||
|
<div id="speech_recognition_settings">
|
||||||
|
<div class="inline-drawer">
|
||||||
|
<div class="inline-drawer-toggle inline-drawer-header">
|
||||||
|
<b>Speech Recognition</b>
|
||||||
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
|
</div>
|
||||||
|
<div class="inline-drawer-content">
|
||||||
|
<div>
|
||||||
|
<span>Select Speech-to-text Provider</span> </br>
|
||||||
|
<select id="speech_recognition_provider">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="speech_recognition_message_mode_div">
|
||||||
|
<span>Message Mode</span> </br>
|
||||||
|
<select id="speech_recognition_message_mode">
|
||||||
|
<option value="append">Append</option>
|
||||||
|
<option value="replace">Replace</option>
|
||||||
|
<option value="auto_send">Auto send</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="speech_recognition_message_mapping_div">
|
||||||
|
<span>Message Mapping</span>
|
||||||
|
<textarea id="speech_recognition_message_mapping" class="text_pole textarea_compact" type="text" rows="4" placeholder="Enter comma separated phrases mapping, example:\ncommand delete = /del 2,\nslash delete = /del 2,\nsystem roll = /roll 2d6,\nhey continue = /continue"></textarea>
|
||||||
|
<span id="speech_recognition_message_mapping_status"></span>
|
||||||
|
<label class="checkbox_label" for="speech_recognition_message_mapping_enabled">
|
||||||
|
<input type="checkbox" id="speech_recognition_message_mapping_enabled" name="speech_recognition_message_mapping_enabled">
|
||||||
|
<small>Enable messages mapping</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<form id="speech_recognition_provider_settings" class="inline-drawer-content">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
$('#extensions_settings').append(settingsHtml);
|
||||||
|
$('#speech_recognition_provider_settings').on('input', onSttProviderSettingsInput);
|
||||||
|
for (const provider in sttProviders) {
|
||||||
|
$('#speech_recognition_provider').append($("<option />").val(provider).text(provider));
|
||||||
|
console.debug(DEBUG_PREFIX+"added option "+provider);
|
||||||
|
}
|
||||||
|
$('#speech_recognition_provider').on('change', onSttProviderChange);
|
||||||
|
$('#speech_recognition_message_mode').on('change', onMessageModeChange);
|
||||||
|
$('#speech_recognition_message_mapping').on('change', onMessageMappingChange);
|
||||||
|
$('#speech_recognition_message_mapping_enabled').on('click', onMessageMappingEnabledClick);
|
||||||
|
|
||||||
|
const $button = $('<div id="microphone_button" class="fa-solid fa-microphone speech-toggle" title="Click to speak"></div>');
|
||||||
$('#send_but_sheld').prepend($button);
|
$('#send_but_sheld').prepend($button);
|
||||||
|
|
||||||
let listening = false;
|
}
|
||||||
$button.on('click', function () {
|
addExtensionControls(); // No init dependencies
|
||||||
if (listening) {
|
loadSettings(); // Depends on Extension Controls and loadTtsProvider
|
||||||
recognition.stop();
|
loadSttProvider(extension_settings.speech_recognition.currentProvider); // No dependencies
|
||||||
} else {
|
|
||||||
recognition.start();
|
|
||||||
}
|
|
||||||
listening = !listening;
|
|
||||||
});
|
|
||||||
|
|
||||||
let initialText = '';
|
//const wrapper = new ModuleWorkerWrapper(moduleWorker);
|
||||||
|
//setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things
|
||||||
recognition.onresult = function (speechEvent) {
|
//moduleWorker();
|
||||||
let finalTranscript = '';
|
})
|
||||||
let interimTranscript = ''
|
|
||||||
|
|
||||||
for (let i = speechEvent.resultIndex; i < speechEvent.results.length; ++i) {
|
|
||||||
const transcript = speechEvent.results[i][0].transcript;
|
|
||||||
|
|
||||||
if (speechEvent.results[i].isFinal) {
|
|
||||||
let interim = capitalizeInterim(transcript);
|
|
||||||
if (interim != '') {
|
|
||||||
let final = finalTranscript;
|
|
||||||
final = composeValues(final, interim) + '.';
|
|
||||||
finalTranscript = final;
|
|
||||||
recognition.abort();
|
|
||||||
listening = false;
|
|
||||||
}
|
|
||||||
interimTranscript = ' ';
|
|
||||||
} else {
|
|
||||||
interimTranscript += transcript;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interimTranscript = capitalizeInterim(interimTranscript);
|
|
||||||
|
|
||||||
$textarea.val(initialText + finalTranscript + interimTranscript);
|
|
||||||
};
|
|
||||||
|
|
||||||
recognition.onerror = function (event) {
|
|
||||||
console.error('Error occurred in recognition:', event.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
recognition.onend = function () {
|
|
||||||
listening = false;
|
|
||||||
$button.toggleClass('fa-microphone fa-microphone-slash');
|
|
||||||
};
|
|
||||||
|
|
||||||
recognition.onstart = function () {
|
|
||||||
initialText = $textarea.val();
|
|
||||||
$button.toggleClass('fa-microphone fa-microphone-slash');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}(jQuery));
|
|
||||||
|
|
||||||
jQuery(() => {
|
|
||||||
const $textarea = $('#send_textarea');
|
|
||||||
$textarea.speechRecognitionPlugin();
|
|
||||||
});
|
|
||||||
|
|
|
@ -2,10 +2,13 @@
|
||||||
"display_name": "Speech Recognition",
|
"display_name": "Speech Recognition",
|
||||||
"loading_order": 13,
|
"loading_order": 13,
|
||||||
"requires": [],
|
"requires": [],
|
||||||
"optional": [],
|
"optional": [
|
||||||
|
"vosk-speech-recognition",
|
||||||
|
"whisper-speech-recognition"
|
||||||
|
],
|
||||||
"js": "index.js",
|
"js": "index.js",
|
||||||
"css": "style.css",
|
"css": "style.css",
|
||||||
"author": "Cohee#1207",
|
"author": "Cohee#1207 and Keij#6799",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { getApiUrl, doExtrasFetch } from "../../extensions.js";
|
||||||
|
export { VoskSttProvider }
|
||||||
|
|
||||||
|
const DEBUG_PREFIX = "<Speech Recognition module (Vosk)> "
|
||||||
|
|
||||||
|
class VoskSttProvider {
|
||||||
|
//########//
|
||||||
|
// Config //
|
||||||
|
//########//
|
||||||
|
|
||||||
|
settings
|
||||||
|
|
||||||
|
defaultSettings = {
|
||||||
|
}
|
||||||
|
|
||||||
|
get settingsHtml() {
|
||||||
|
let html = ""
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsChange() {
|
||||||
|
// Used when provider settings are updated from UI
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings(settings) {
|
||||||
|
// Populate Provider UI given input settings
|
||||||
|
if (Object.keys(settings).length == 0) {
|
||||||
|
console.debug(DEBUG_PREFIX+"Using default vosk STT extension settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept keys defined in defaultSettings
|
||||||
|
this.settings = this.defaultSettings
|
||||||
|
|
||||||
|
for (const key in settings){
|
||||||
|
if (key in this.settings){
|
||||||
|
this.settings[key] = settings[key]
|
||||||
|
} else {
|
||||||
|
throw `Invalid setting passed to STT extension: ${key}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX+"Vosk STT settings loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
async processAudio(audioblob) {
|
||||||
|
var requestData = new FormData();
|
||||||
|
requestData.append('AudioFile', audioblob, 'record.wav');
|
||||||
|
|
||||||
|
const url = new URL(getApiUrl());
|
||||||
|
url.pathname = '/api/speech-recognition/vosk/process-audio';
|
||||||
|
|
||||||
|
const apiResult = await doExtrasFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: requestData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!apiResult.ok) {
|
||||||
|
toastr.error(apiResult.statusText, 'STT Generation Failed (Vosk)', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await apiResult.json();
|
||||||
|
return result.transcript;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { getApiUrl, doExtrasFetch } from "../../extensions.js";
|
||||||
|
export { WhisperSttProvider }
|
||||||
|
|
||||||
|
const DEBUG_PREFIX = "<Speech Recognition module (Vosk)> "
|
||||||
|
|
||||||
|
class WhisperSttProvider {
|
||||||
|
//########//
|
||||||
|
// Config //
|
||||||
|
//########//
|
||||||
|
|
||||||
|
settings
|
||||||
|
|
||||||
|
defaultSettings = {
|
||||||
|
//model_path: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
get settingsHtml() {
|
||||||
|
let html = ""
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsChange() {
|
||||||
|
// Used when provider settings are updated from UI
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings(settings) {
|
||||||
|
// Populate Provider UI given input settings
|
||||||
|
if (Object.keys(settings).length == 0) {
|
||||||
|
console.debug(DEBUG_PREFIX+"Using default Whisper STT extension settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only accept keys defined in defaultSettings
|
||||||
|
this.settings = this.defaultSettings
|
||||||
|
|
||||||
|
for (const key in settings){
|
||||||
|
if (key in this.settings){
|
||||||
|
this.settings[key] = settings[key]
|
||||||
|
} else {
|
||||||
|
throw `Invalid setting passed to STT extension: ${key}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(DEBUG_PREFIX+"Whisper STT settings loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
async processAudio(audioblob) {
|
||||||
|
var requestData = new FormData();
|
||||||
|
requestData.append('AudioFile', audioblob, 'record.wav');
|
||||||
|
|
||||||
|
const url = new URL(getApiUrl());
|
||||||
|
url.pathname = '/api/speech-recognition/whisper/process-audio';
|
||||||
|
|
||||||
|
const apiResult = await doExtrasFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: requestData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!apiResult.ok) {
|
||||||
|
toastr.error(apiResult.statusText, 'STT Generation Failed (Whisper)', { timeOut: 10000, extendedTimeOut: 20000, preventDuplicates: true });
|
||||||
|
throw new Error(`HTTP ${apiResult.status}: ${await apiResult.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await apiResult.json();
|
||||||
|
return result.transcript;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -555,7 +555,8 @@ async function generatePicture(_, trigger, message, callback) {
|
||||||
|
|
||||||
const prevSDHeight = extension_settings.sd.height;
|
const prevSDHeight = extension_settings.sd.height;
|
||||||
if (generationType == generationMode.FACE) {
|
if (generationType == generationMode.FACE) {
|
||||||
extension_settings.sd.height = extension_settings.sd.width * 1.5;
|
// Round to nearest multiple of 64
|
||||||
|
extension_settings.sd.height = Math.round(extension_settings.sd.height * 1.5 / 64) * 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -62,6 +62,8 @@ function loadNovelPreset(preset) {
|
||||||
nai_settings.top_a = preset.top_a;
|
nai_settings.top_a = preset.top_a;
|
||||||
nai_settings.typical_p = preset.typical_p;
|
nai_settings.typical_p = preset.typical_p;
|
||||||
nai_settings.min_length = preset.min_length;
|
nai_settings.min_length = preset.min_length;
|
||||||
|
nai_settings.cfg_scale = preset.cfg_scale;
|
||||||
|
nai_settings.phrase_rep_pen = preset.phrase_rep_pen;
|
||||||
loadNovelSettingsUi(nai_settings);
|
loadNovelSettingsUi(nai_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,14 +86,42 @@ function loadNovelSettings(settings) {
|
||||||
nai_settings.top_a = settings.top_a;
|
nai_settings.top_a = settings.top_a;
|
||||||
nai_settings.typical_p = settings.typical_p;
|
nai_settings.typical_p = settings.typical_p;
|
||||||
nai_settings.min_length = settings.min_length;
|
nai_settings.min_length = settings.min_length;
|
||||||
|
nai_settings.phrase_rep_pen = settings.phrase_rep_pen;
|
||||||
|
nai_settings.cfg_scale = settings.cfg_scale;
|
||||||
nai_settings.streaming_novel = !!settings.streaming_novel;
|
nai_settings.streaming_novel = !!settings.streaming_novel;
|
||||||
loadNovelSettingsUi(nai_settings);
|
loadNovelSettingsUi(nai_settings);
|
||||||
|
}
|
||||||
|
|
||||||
// reload the preset to migrate any new settings
|
const phraseRepPenStrings = [
|
||||||
for (const key of Object.keys(nai_settings)) {
|
null,
|
||||||
if (typeof nai_settings[key] === 'number' && Number.isNaN(nai_settings[key])) {
|
"very_light",
|
||||||
$("#settings_perset_novel").trigger("change");
|
"light",
|
||||||
}
|
"medium",
|
||||||
|
"aggressive",
|
||||||
|
"very_aggressive"
|
||||||
|
]
|
||||||
|
|
||||||
|
function getPhraseRepPenString(phraseRepPenCounter) {
|
||||||
|
if (phraseRepPenCounter < 1 || phraseRepPenCounter > 5) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return phraseRepPenStrings[phraseRepPenCounter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPhraseRepPenCounter(phraseRepPenString) {
|
||||||
|
if (phraseRepPenString === phraseRepPenStrings[1]) {
|
||||||
|
return 1;
|
||||||
|
} else if (phraseRepPenString === phraseRepPenStrings[2]) {
|
||||||
|
return 2;
|
||||||
|
} else if (phraseRepPenString === phraseRepPenStrings[3]) {
|
||||||
|
return 3;
|
||||||
|
} else if (phraseRepPenString === phraseRepPenStrings[4]) {
|
||||||
|
return 4;
|
||||||
|
} else if (phraseRepPenString === phraseRepPenStrings[5]) {
|
||||||
|
return 5;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +148,10 @@ function loadNovelSettingsUi(ui_settings) {
|
||||||
$("#top_a_counter_novel").text(Number(ui_settings.top_a).toFixed(2));
|
$("#top_a_counter_novel").text(Number(ui_settings.top_a).toFixed(2));
|
||||||
$("#typical_p_novel").val(ui_settings.typical_p);
|
$("#typical_p_novel").val(ui_settings.typical_p);
|
||||||
$("#typical_p_counter_novel").text(Number(ui_settings.typical_p).toFixed(2));
|
$("#typical_p_counter_novel").text(Number(ui_settings.typical_p).toFixed(2));
|
||||||
|
$("#cfg_scale_novel").val(ui_settings.cfg_scale);
|
||||||
|
$("#cfg_scale_counter_novel").text(Number(ui_settings.cfg_scale).toFixed(2));
|
||||||
|
$("#phrase_rep_pen_novel").val(getPhraseRepPenCounter(ui_settings.phrase_rep_pen));
|
||||||
|
$("#phrase_rep_pen_counter_novel").text(getPhraseRepPenCounter(ui_settings.phrase_rep_pen));
|
||||||
$("#min_length_novel").val(ui_settings.min_length);
|
$("#min_length_novel").val(ui_settings.min_length);
|
||||||
$("#min_length_counter_novel").text(Number(ui_settings.min_length).toFixed(0));
|
$("#min_length_counter_novel").text(Number(ui_settings.min_length).toFixed(0));
|
||||||
|
|
||||||
|
@ -191,6 +225,18 @@ const sliders = [
|
||||||
format: (val) => Number(val).toFixed(2),
|
format: (val) => Number(val).toFixed(2),
|
||||||
setValue: (val) => { nai_settings.typical_p = Number(val).toFixed(2); },
|
setValue: (val) => { nai_settings.typical_p = Number(val).toFixed(2); },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sliderId: "#cfg_scale_novel",
|
||||||
|
counterId: "#cfg_scale_counter_novel",
|
||||||
|
format: (val) => `${val}`,
|
||||||
|
setValue: (val) => { nai_settings.cfg_scale = Number(val).toFixed(2); },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sliderId: "#phrase_rep_pen_novel",
|
||||||
|
counterId: "#phrase_rep_pen_counter_novel",
|
||||||
|
format: (val) => `${val}`,
|
||||||
|
setValue: (val) => { nai_settings.phrase_rep_pen = getPhraseRepPenString(Number(val).toFixed(0)); },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sliderId: "#min_length_novel",
|
sliderId: "#min_length_novel",
|
||||||
counterId: "#min_length_counter_novel",
|
counterId: "#min_length_counter_novel",
|
||||||
|
@ -200,6 +246,7 @@ const sliders = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getNovelGenerationData(finalPromt, this_settings, this_amount_gen) {
|
export function getNovelGenerationData(finalPromt, this_settings, this_amount_gen) {
|
||||||
|
const isNewModel = (nai_settings.model_novel.includes('clio') || nai_settings.model_novel.includes('kayra'));
|
||||||
return {
|
return {
|
||||||
"input": finalPromt,
|
"input": finalPromt,
|
||||||
"model": nai_settings.model_novel,
|
"model": nai_settings.model_novel,
|
||||||
|
@ -217,13 +264,16 @@ export function getNovelGenerationData(finalPromt, this_settings, this_amount_ge
|
||||||
"top_p": parseFloat(nai_settings.top_p),
|
"top_p": parseFloat(nai_settings.top_p),
|
||||||
"top_k": parseInt(nai_settings.top_k),
|
"top_k": parseInt(nai_settings.top_k),
|
||||||
"typical_p": parseFloat(nai_settings.typical_p),
|
"typical_p": parseFloat(nai_settings.typical_p),
|
||||||
|
"cfg_scale": parseFloat(nai_settings.cfg_scale),
|
||||||
|
"cfg_uc": "",
|
||||||
|
"phrase_rep_pen": nai_settings.phrase_rep_pen,
|
||||||
//"stop_sequences": {{187}},
|
//"stop_sequences": {{187}},
|
||||||
//bad_words_ids = {{50256}, {0}, {1}};
|
//bad_words_ids = {{50256}, {0}, {1}};
|
||||||
"generate_until_sentence": true,
|
"generate_until_sentence": true,
|
||||||
"use_cache": false,
|
"use_cache": false,
|
||||||
"use_string": true,
|
"use_string": true,
|
||||||
"return_full_text": false,
|
"return_full_text": false,
|
||||||
"prefix": "vanilla",
|
"prefix": isNewModel ? "special_instruct" : "vanilla",
|
||||||
"order": this_settings.order,
|
"order": this_settings.order,
|
||||||
"streaming": nai_settings.streaming_novel,
|
"streaming": nai_settings.streaming_novel,
|
||||||
};
|
};
|
||||||
|
|
|
@ -142,6 +142,8 @@ const default_settings = {
|
||||||
max_context_unlocked: false,
|
max_context_unlocked: false,
|
||||||
api_url_scale: '',
|
api_url_scale: '',
|
||||||
show_external_models: false,
|
show_external_models: false,
|
||||||
|
proxy_password: '',
|
||||||
|
assistant_prefill: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const oai_settings = {
|
const oai_settings = {
|
||||||
|
@ -178,6 +180,8 @@ const oai_settings = {
|
||||||
max_context_unlocked: false,
|
max_context_unlocked: false,
|
||||||
api_url_scale: '',
|
api_url_scale: '',
|
||||||
show_external_models: false,
|
show_external_models: false,
|
||||||
|
proxy_password: '',
|
||||||
|
assistant_prefill: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
let openai_setting_names;
|
let openai_setting_names;
|
||||||
|
@ -767,11 +771,13 @@ async function sendOpenAIRequest(type, openai_msgs_tosend, signal) {
|
||||||
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI].includes(oai_settings.chat_completion_source)) {
|
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI].includes(oai_settings.chat_completion_source)) {
|
||||||
validateReverseProxy();
|
validateReverseProxy();
|
||||||
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
|
||||||
|
generate_data['proxy_password'] = oai_settings.proxy_password;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClaude) {
|
if (isClaude) {
|
||||||
generate_data['use_claude'] = true;
|
generate_data['use_claude'] = true;
|
||||||
generate_data['top_k'] = parseFloat(oai_settings.top_k_openai);
|
generate_data['top_k'] = parseFloat(oai_settings.top_k_openai);
|
||||||
|
generate_data['assistant_prefill'] = substituteParams(oai_settings.assistant_prefill);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOpenRouter) {
|
if (isOpenRouter) {
|
||||||
|
@ -1105,6 +1111,8 @@ function loadOpenAISettings(data, settings) {
|
||||||
oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;
|
oai_settings.chat_completion_source = settings.chat_completion_source ?? default_settings.chat_completion_source;
|
||||||
oai_settings.api_url_scale = settings.api_url_scale ?? default_settings.api_url_scale;
|
oai_settings.api_url_scale = settings.api_url_scale ?? default_settings.api_url_scale;
|
||||||
oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;
|
oai_settings.show_external_models = settings.show_external_models ?? default_settings.show_external_models;
|
||||||
|
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
|
||||||
|
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
|
||||||
|
|
||||||
if (settings.nsfw_toggle !== undefined) oai_settings.nsfw_toggle = !!settings.nsfw_toggle;
|
if (settings.nsfw_toggle !== undefined) oai_settings.nsfw_toggle = !!settings.nsfw_toggle;
|
||||||
if (settings.keep_example_dialogue !== undefined) oai_settings.keep_example_dialogue = !!settings.keep_example_dialogue;
|
if (settings.keep_example_dialogue !== undefined) oai_settings.keep_example_dialogue = !!settings.keep_example_dialogue;
|
||||||
|
@ -1116,6 +1124,8 @@ function loadOpenAISettings(data, settings) {
|
||||||
|
|
||||||
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
$('#stream_toggle').prop('checked', oai_settings.stream_openai);
|
||||||
$('#api_url_scale').val(oai_settings.api_url_scale);
|
$('#api_url_scale').val(oai_settings.api_url_scale);
|
||||||
|
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
||||||
|
$('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
|
||||||
|
|
||||||
$('#model_openai_select').val(oai_settings.openai_model);
|
$('#model_openai_select').val(oai_settings.openai_model);
|
||||||
$(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true);
|
$(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true);
|
||||||
|
@ -1169,9 +1179,7 @@ function loadOpenAISettings(data, settings) {
|
||||||
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
|
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
|
||||||
$('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
|
$('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
|
||||||
|
|
||||||
if (oai_settings.reverse_proxy !== '') {
|
$(".reverse_proxy_warning").toggle(oai_settings.reverse_proxy !== '');
|
||||||
$("#ReverseProxyWarningMessage").css('display', 'block');
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#openai_logit_bias_preset').empty();
|
$('#openai_logit_bias_preset').empty();
|
||||||
for (const preset of Object.keys(oai_settings.bias_presets)) {
|
for (const preset of Object.keys(oai_settings.bias_presets)) {
|
||||||
|
@ -1212,6 +1220,7 @@ async function getStatusOpen() {
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
reverse_proxy: oai_settings.reverse_proxy,
|
reverse_proxy: oai_settings.reverse_proxy,
|
||||||
|
proxy_password: oai_settings.proxy_password,
|
||||||
use_openrouter: oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER,
|
use_openrouter: oai_settings.chat_completion_source == chat_completion_sources.OPENROUTER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1311,6 +1320,7 @@ async function saveOpenAIPreset(name, settings) {
|
||||||
impersonation_prompt: settings.impersonation_prompt,
|
impersonation_prompt: settings.impersonation_prompt,
|
||||||
bias_preset_selected: settings.bias_preset_selected,
|
bias_preset_selected: settings.bias_preset_selected,
|
||||||
reverse_proxy: settings.reverse_proxy,
|
reverse_proxy: settings.reverse_proxy,
|
||||||
|
proxy_password: settings.proxy_password,
|
||||||
legacy_streaming: settings.legacy_streaming,
|
legacy_streaming: settings.legacy_streaming,
|
||||||
max_context_unlocked: settings.max_context_unlocked,
|
max_context_unlocked: settings.max_context_unlocked,
|
||||||
nsfw_avoidance_prompt: settings.nsfw_avoidance_prompt,
|
nsfw_avoidance_prompt: settings.nsfw_avoidance_prompt,
|
||||||
|
@ -1318,6 +1328,7 @@ async function saveOpenAIPreset(name, settings) {
|
||||||
stream_openai: settings.stream_openai,
|
stream_openai: settings.stream_openai,
|
||||||
api_url_scale: settings.api_url_scale,
|
api_url_scale: settings.api_url_scale,
|
||||||
show_external_models: settings.show_external_models,
|
show_external_models: settings.show_external_models,
|
||||||
|
assistant_prefill: settings.assistant_prefill,
|
||||||
};
|
};
|
||||||
|
|
||||||
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
const savePresetSettings = await fetch(`/savepreset_openai?name=${name}`, {
|
||||||
|
@ -1650,6 +1661,8 @@ function onSettingsPresetChange() {
|
||||||
stream_openai: ['#stream_toggle', 'stream_openai', true],
|
stream_openai: ['#stream_toggle', 'stream_openai', true],
|
||||||
api_url_scale: ['#api_url_scale', 'api_url_scale', false],
|
api_url_scale: ['#api_url_scale', 'api_url_scale', false],
|
||||||
show_external_models: ['#openai_show_external_models', 'show_external_models', true],
|
show_external_models: ['#openai_show_external_models', 'show_external_models', true],
|
||||||
|
proxy_password: ['#openai_proxy_password', 'proxy_password', false],
|
||||||
|
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
|
for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) {
|
||||||
|
@ -1858,9 +1871,7 @@ async function onNewPresetClick() {
|
||||||
|
|
||||||
function onReverseProxyInput() {
|
function onReverseProxyInput() {
|
||||||
oai_settings.reverse_proxy = $(this).val();
|
oai_settings.reverse_proxy = $(this).val();
|
||||||
if (oai_settings.reverse_proxy == '') {
|
$(".reverse_proxy_warning").toggle(oai_settings.reverse_proxy != '');
|
||||||
$("#ReverseProxyWarningMessage").css('display', 'none');
|
|
||||||
} else { $("#ReverseProxyWarningMessage").css('display', 'block'); }
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1912,7 +1923,7 @@ async function onConnectButtonClick(e) {
|
||||||
await writeSecret(SECRET_KEYS.CLAUDE, api_key_claude);
|
await writeSecret(SECRET_KEYS.CLAUDE, api_key_claude);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret_state[SECRET_KEYS.CLAUDE]) {
|
if (!secret_state[SECRET_KEYS.CLAUDE] && !oai_settings.reverse_proxy) {
|
||||||
console.log('No secret key saved for Claude');
|
console.log('No secret key saved for Claude');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1925,7 +1936,7 @@ async function onConnectButtonClick(e) {
|
||||||
await writeSecret(SECRET_KEYS.OPENAI, api_key_openai);
|
await writeSecret(SECRET_KEYS.OPENAI, api_key_openai);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secret_state[SECRET_KEYS.OPENAI]) {
|
if (!secret_state[SECRET_KEYS.OPENAI] && !oai_settings.reverse_proxy) {
|
||||||
console.log('No secret key saved for OpenAI');
|
console.log('No secret key saved for OpenAI');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2197,6 +2208,16 @@ $(document).ready(function () {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#openai_proxy_password').on('input', function () {
|
||||||
|
oai_settings.proxy_password = $(this).val();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#claude_assistant_prefill').on('input', function () {
|
||||||
|
oai_settings.assistant_prefill = $(this).val();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
$("#api_button_openai").on("click", onConnectButtonClick);
|
$("#api_button_openai").on("click", onConnectButtonClick);
|
||||||
$("#openai_reverse_proxy").on("input", onReverseProxyInput);
|
$("#openai_reverse_proxy").on("input", onReverseProxyInput);
|
||||||
$("#model_openai_select").on("change", onModelChange);
|
$("#model_openai_select").on("change", onModelChange);
|
||||||
|
|
|
@ -186,6 +186,7 @@ let power_user = {
|
||||||
persona_description_position: persona_description_positions.BEFORE_CHAR,
|
persona_description_position: persona_description_positions.BEFORE_CHAR,
|
||||||
|
|
||||||
custom_stopping_strings: '',
|
custom_stopping_strings: '',
|
||||||
|
fuzzy_search: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let themes = [];
|
let themes = [];
|
||||||
|
@ -255,7 +256,7 @@ function fixMarkdown(text) {
|
||||||
// i.e. "^example * text* * harder problem *\n" -> "^example *text* *harder problem*\n"
|
// i.e. "^example * text* * harder problem *\n" -> "^example *text* *harder problem*\n"
|
||||||
|
|
||||||
// Find pairs of formatting characters and capture the text in between them
|
// Find pairs of formatting characters and capture the text in between them
|
||||||
const format = /(\*|_|~){1,2}([\s\S]*?)\1{1,2}/gm;
|
const format = /([\*_]{1,2})([\s\S]*?)\1/gm;
|
||||||
let matches = [];
|
let matches = [];
|
||||||
let match;
|
let match;
|
||||||
while ((match = format.exec(text)) !== null) {
|
while ((match = format.exec(text)) !== null) {
|
||||||
|
@ -266,7 +267,7 @@ function fixMarkdown(text) {
|
||||||
let newText = text;
|
let newText = text;
|
||||||
for (let i = matches.length - 1; i >= 0; i--) {
|
for (let i = matches.length - 1; i >= 0; i--) {
|
||||||
let matchText = matches[i][0];
|
let matchText = matches[i][0];
|
||||||
let replacementText = matchText.replace(/(\*|_|~)(\s+)|(\s+)(\*|_|~)/g, '$1$4');
|
let replacementText = matchText.replace(/(\*|_)([\t \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+)|([\t \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+)(\*|_)/g, '$1$4');
|
||||||
newText = newText.slice(0, matches[i].index) + replacementText + newText.slice(matches[i].index + matchText.length);
|
newText = newText.slice(0, matches[i].index) + replacementText + newText.slice(matches[i].index + matchText.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,6 +677,7 @@ function loadPowerUserSettings(settings, data) {
|
||||||
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
|
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
|
||||||
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
|
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
|
||||||
$('#custom_stopping_strings').val(power_user.custom_stopping_strings);
|
$('#custom_stopping_strings').val(power_user.custom_stopping_strings);
|
||||||
|
$('#fuzzy_search_checkbox').prop("checked", power_user.fuzzy_search);
|
||||||
|
|
||||||
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
|
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
|
||||||
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
|
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
|
||||||
|
@ -896,6 +898,31 @@ function loadInstructMode() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fuzzySearchCharacters(searchValue) {
|
||||||
|
const fuse = new Fuse(characters, {
|
||||||
|
keys: [
|
||||||
|
{ name: 'data.name', weight: 5 },
|
||||||
|
{ name: 'data.description', weight: 3 },
|
||||||
|
{ name: 'data.mes_example', weight: 3 },
|
||||||
|
{ name: 'data.scenario', weight: 2 },
|
||||||
|
{ name: 'data.personality', weight: 2 },
|
||||||
|
{ name: 'data.first_mes', weight: 2 },
|
||||||
|
{ name: 'data.creator_notes', weight: 2 },
|
||||||
|
{ name: 'data.creator', weight: 1 },
|
||||||
|
{ name: 'data.tags', weight: 1 },
|
||||||
|
{ name: 'data.alternate_greetings', weight: 1 }
|
||||||
|
],
|
||||||
|
includeScore: true,
|
||||||
|
ignoreLocation: true,
|
||||||
|
threshold: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = fuse.search(searchValue);
|
||||||
|
console.debug('Fuzzy search results for ' + searchValue, results)
|
||||||
|
const indices = results.map(x => x.refIndex);
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) {
|
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) {
|
||||||
const includeNames = isNarrator ? false : (power_user.instruct.names || !!selected_group || !!forceAvatar);
|
const includeNames = isNarrator ? false : (power_user.instruct.names || !!selected_group || !!forceAvatar);
|
||||||
let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence;
|
let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence;
|
||||||
|
@ -1972,6 +1999,11 @@ $(document).ready(() => {
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#fuzzy_search_checkbox').on('input', function () {
|
||||||
|
power_user.fuzzy_search = !!$(this).prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
$(window).on('focus', function () {
|
$(window).on('focus', function () {
|
||||||
browser_has_focus = true;
|
browser_has_focus = true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -970,7 +970,7 @@ async function checkWorldInfo(chat, maxContext) {
|
||||||
if (selectiveLogic === 0) {
|
if (selectiveLogic === 0) {
|
||||||
console.debug('saw AND logic, checking..')
|
console.debug('saw AND logic, checking..')
|
||||||
if (secondarySubstituted && matchKeys(textToScan, secondarySubstituted.trim())) {
|
if (secondarySubstituted && matchKeys(textToScan, secondarySubstituted.trim())) {
|
||||||
console.log(`activating entry ${entry.uid} with AND found`)
|
console.debug(`activating entry ${entry.uid} with AND found`)
|
||||||
activatedNow.add(entry);
|
activatedNow.add(entry);
|
||||||
break secondary;
|
break secondary;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1512,8 +1512,20 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.character_name_block {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.ch_avatar_url {
|
.ch_avatar_url {
|
||||||
float: right;
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.charListGrid #rm_print_characters_block .character_version,
|
||||||
|
body.charListGrid #rm_print_characters_block .ch_avatar_url {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character_select .avatar {
|
.character_select .avatar {
|
||||||
|
@ -1547,7 +1559,7 @@ body.big-avatars .ch_description {
|
||||||
|
|
||||||
/*applies to both groups and solos chars in the char list*/
|
/*applies to both groups and solos chars in the char list*/
|
||||||
#rm_print_characters_block .ch_name {
|
#rm_print_characters_block .ch_name {
|
||||||
width: 100%;
|
flex: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* max-width: calc(100% - 29px); */
|
/* max-width: calc(100% - 29px); */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -4504,14 +4516,16 @@ toolcool-color-picker {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openai_preset_buttons, .preset_buttons {
|
.openai_preset_buttons,
|
||||||
|
.preset_buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openai_preset_buttons select, .preset_buttons select {
|
.openai_preset_buttons select,
|
||||||
|
.preset_buttons select {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
100
server.js
100
server.js
|
@ -290,6 +290,7 @@ const directories = {
|
||||||
instruct: 'public/instruct',
|
instruct: 'public/instruct',
|
||||||
context: 'public/context',
|
context: 'public/context',
|
||||||
backups: 'backups/',
|
backups: 'backups/',
|
||||||
|
quickreplies: 'public/QuickReplies'
|
||||||
};
|
};
|
||||||
|
|
||||||
// CSRF Protection //
|
// CSRF Protection //
|
||||||
|
@ -1224,6 +1225,11 @@ const calculateChatSize = (charDir) => {
|
||||||
return { chatSize, dateLastChat };
|
return { chatSize, dateLastChat };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the total string length of the data object
|
||||||
|
const calculateDataSize = (data) => {
|
||||||
|
return typeof data === 'object' ? Object.values(data).reduce((acc, val) => acc + new String(val).length, 0) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* processCharacter - Process a given character, read its data and calculate its statistics.
|
* processCharacter - Process a given character, read its data and calculate its statistics.
|
||||||
*
|
*
|
||||||
|
@ -1245,6 +1251,7 @@ const processCharacter = async (item, i) => {
|
||||||
const { chatSize, dateLastChat } = calculateChatSize(char_dir);
|
const { chatSize, dateLastChat } = calculateChatSize(char_dir);
|
||||||
characters[i]['chat_size'] = chatSize;
|
characters[i]['chat_size'] = chatSize;
|
||||||
characters[i]['date_last_chat'] = dateLastChat;
|
characters[i]['date_last_chat'] = dateLastChat;
|
||||||
|
characters[i]['data_size'] = calculateDataSize(jsonObject?.data);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
characters[i] = {
|
characters[i] = {
|
||||||
|
@ -1594,6 +1601,8 @@ app.post('/getsettings', jsonParser, (request, response) => {
|
||||||
|
|
||||||
const themes = readAndParseFromDirectory(directories.themes);
|
const themes = readAndParseFromDirectory(directories.themes);
|
||||||
const movingUIPresets = readAndParseFromDirectory(directories.movingUI);
|
const movingUIPresets = readAndParseFromDirectory(directories.movingUI);
|
||||||
|
const quickReplyPresets = readAndParseFromDirectory(directories.quickreplies);
|
||||||
|
|
||||||
const instruct = readAndParseFromDirectory(directories.instruct);
|
const instruct = readAndParseFromDirectory(directories.instruct);
|
||||||
const context = readAndParseFromDirectory(directories.context);
|
const context = readAndParseFromDirectory(directories.context);
|
||||||
|
|
||||||
|
@ -1610,6 +1619,7 @@ app.post('/getsettings', jsonParser, (request, response) => {
|
||||||
textgenerationwebui_preset_names,
|
textgenerationwebui_preset_names,
|
||||||
themes,
|
themes,
|
||||||
movingUIPresets,
|
movingUIPresets,
|
||||||
|
quickReplyPresets,
|
||||||
instruct,
|
instruct,
|
||||||
context,
|
context,
|
||||||
enable_extensions: enableExtensions,
|
enable_extensions: enableExtensions,
|
||||||
|
@ -1666,6 +1676,17 @@ app.post('/savemovingui', jsonParser, (request, response) => {
|
||||||
return response.sendStatus(200);
|
return response.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/savequickreply', jsonParser, (request, response) => {
|
||||||
|
if (!request.body || !request.body.name) {
|
||||||
|
return response.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = path.join(directories.quickreplies, sanitize(request.body.name) + '.json');
|
||||||
|
fs.writeFileSync(filename, JSON.stringify(request.body, null, 4), 'utf8');
|
||||||
|
|
||||||
|
return response.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
function convertWorldInfoToCharacterBook(name, entries) {
|
function convertWorldInfoToCharacterBook(name, entries) {
|
||||||
const result = { entries: [], name };
|
const result = { entries: [], name };
|
||||||
|
|
||||||
|
@ -1780,7 +1801,7 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
|
||||||
|
|
||||||
console.log(request.body);
|
console.log(request.body);
|
||||||
const bw = require('./src/bad-words');
|
const bw = require('./src/bad-words');
|
||||||
const bad_words_ids = request.body.model.includes('clio') ? bw.clioBadWordsId : bw.badWordIds;
|
const isNewModel = (request.body.model.includes('clio') || request.body.model.includes('kayra'));
|
||||||
const data = {
|
const data = {
|
||||||
"input": request.body.input,
|
"input": request.body.input,
|
||||||
"model": request.body.model,
|
"model": request.body.model,
|
||||||
|
@ -1799,13 +1820,16 @@ app.post("/generate_novelai", jsonParser, async function (request, response_gene
|
||||||
"top_p": request.body.top_p,
|
"top_p": request.body.top_p,
|
||||||
"top_k": request.body.top_k,
|
"top_k": request.body.top_k,
|
||||||
"typical_p": request.body.typical_p,
|
"typical_p": request.body.typical_p,
|
||||||
|
"cfg_scale": request.body.cfg_scale,
|
||||||
|
"cfg_uc": request.body.cfg_uc,
|
||||||
|
"phrase_rep_pen": request.body.phrase_rep_pen,
|
||||||
//"stop_sequences": {{187}},
|
//"stop_sequences": {{187}},
|
||||||
"bad_words_ids": bad_words_ids,
|
"bad_words_ids": isNewModel ? bw.clioBadWordsId : bw.badWordIds,
|
||||||
//generate_until_sentence = true;
|
//generate_until_sentence = true;
|
||||||
"use_cache": request.body.use_cache,
|
"use_cache": request.body.use_cache,
|
||||||
"use_string": true,
|
"use_string": true,
|
||||||
"return_full_text": request.body.return_full_text,
|
"return_full_text": request.body.return_full_text,
|
||||||
"prefix": request.body.prefix,
|
"prefix": isNewModel ? "special_instruct" : request.body.prefix,
|
||||||
"order": request.body.order
|
"order": request.body.order
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2849,7 +2873,7 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_
|
||||||
|
|
||||||
if (request.body.use_openrouter == false) {
|
if (request.body.use_openrouter == false) {
|
||||||
api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
||||||
api_key_openai = readSecret(SECRET_KEYS.OPENAI);
|
api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.OPENAI);
|
||||||
headers = {};
|
headers = {};
|
||||||
} else {
|
} else {
|
||||||
api_url = 'https://openrouter.ai/api/v1';
|
api_url = 'https://openrouter.ai/api/v1';
|
||||||
|
@ -2858,7 +2882,7 @@ app.post("/getstatus_openai", jsonParser, function (request, response_getstatus_
|
||||||
headers = { 'HTTP-Referer': request.headers.referer };
|
headers = { 'HTTP-Referer': request.headers.referer };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!api_key_openai) {
|
if (!api_key_openai && !request.body.reverse_proxy) {
|
||||||
return response_getstatus_openai.status(401).send({ error: true });
|
return response_getstatus_openai.status(401).send({ error: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2927,44 +2951,6 @@ app.post("/openai_bias", jsonParser, async function (request, response) {
|
||||||
return response.send(result);
|
return response.send(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Dead code, consider deleting. Users will get redirected to OpenAI site instead.
|
|
||||||
// 'Your request to GET /v1/dashboard/billing/usage must be made with a session key (that is, it can only be made from the browser). You made it with the following key type: secret.'
|
|
||||||
app.post("/openai_usage", jsonParser, async function (request, response) {
|
|
||||||
if (!request.body) return response.sendStatus(400);
|
|
||||||
const key = readSecret(SECRET_KEYS.OPENAI);
|
|
||||||
|
|
||||||
if (!key) {
|
|
||||||
console.warn('Get key usage failed: Missing OpenAI API key.');
|
|
||||||
return response.sendStatus(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
const api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: `Bearer ${key}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const date = new Date();
|
|
||||||
date.setDate(1);
|
|
||||||
const start_date = date.toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
date.setMonth(date.getMonth() + 1);
|
|
||||||
const end_date = date.toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await getAsync(
|
|
||||||
`${api_url}/dashboard/billing/usage?start_date=${start_date}&end_date=${end_date}`,
|
|
||||||
{ headers },
|
|
||||||
);
|
|
||||||
return response.send(res);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return response.sendStatus(400);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/deletepreset_openai", jsonParser, function (request, response) {
|
app.post("/deletepreset_openai", jsonParser, function (request, response) {
|
||||||
if (!request.body || !request.body.name) {
|
if (!request.body || !request.body.name) {
|
||||||
return response.sendStatus(400);
|
return response.sendStatus(400);
|
||||||
|
@ -3093,7 +3079,7 @@ async function sendClaudeRequest(request, response) {
|
||||||
const fetch = require('node-fetch').default;
|
const fetch = require('node-fetch').default;
|
||||||
|
|
||||||
const api_url = new URL(request.body.reverse_proxy || api_claude).toString();
|
const api_url = new URL(request.body.reverse_proxy || api_claude).toString();
|
||||||
const api_key_claude = readSecret(SECRET_KEYS.CLAUDE);
|
const api_key_claude = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.CLAUDE);
|
||||||
|
|
||||||
if (!api_key_claude) {
|
if (!api_key_claude) {
|
||||||
return response.status(401).send({ error: true });
|
return response.status(401).send({ error: true });
|
||||||
|
@ -3106,7 +3092,12 @@ async function sendClaudeRequest(request, response) {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
const requestPrompt = convertClaudePrompt(request.body.messages, true, true);
|
let requestPrompt = convertClaudePrompt(request.body.messages, true, true);
|
||||||
|
|
||||||
|
if (request.body.assistant_prefill) {
|
||||||
|
requestPrompt += request.body.assistant_prefill;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Claude request:', requestPrompt);
|
console.log('Claude request:', requestPrompt);
|
||||||
|
|
||||||
const generateResponse = await fetch(api_url + '/complete', {
|
const generateResponse = await fetch(api_url + '/complete', {
|
||||||
|
@ -3182,7 +3173,7 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
|
||||||
|
|
||||||
if (!request.body.use_openrouter) {
|
if (!request.body.use_openrouter) {
|
||||||
api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
api_url = new URL(request.body.reverse_proxy || api_openai).toString();
|
||||||
api_key_openai = readSecret(SECRET_KEYS.OPENAI);
|
api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.OPENAI);
|
||||||
headers = {};
|
headers = {};
|
||||||
} else {
|
} else {
|
||||||
api_url = 'https://openrouter.ai/api/v1';
|
api_url = 'https://openrouter.ai/api/v1';
|
||||||
|
@ -3191,7 +3182,7 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
|
||||||
headers = { 'HTTP-Referer': request.headers.referer };
|
headers = { 'HTTP-Referer': request.headers.referer };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!api_key_openai) {
|
if (!api_key_openai && !request.body.reverse_proxy) {
|
||||||
return response_generate_openai.status(401).send({ error: true });
|
return response_generate_openai.status(401).send({ error: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3289,6 +3280,10 @@ app.post("/generate_openai", jsonParser, function (request, response_generate_op
|
||||||
message = 'API key disabled or exhausted';
|
message = 'API key disabled or exhausted';
|
||||||
console.log(message);
|
console.log(message);
|
||||||
break;
|
break;
|
||||||
|
case 451:
|
||||||
|
message = error?.response?.data?.error?.message || 'Unavailable for legal reasons';
|
||||||
|
console.log(message);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const quota_error = error?.response?.status === 429 && error?.response?.data?.error?.type === 'insufficient_quota';
|
const quota_error = error?.response?.status === 429 && error?.response?.data?.error?.type === 'insufficient_quota';
|
||||||
|
@ -3448,17 +3443,6 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
|
||||||
|
|
||||||
// ** REST CLIENT ASYNC WRAPPERS **
|
// ** REST CLIENT ASYNC WRAPPERS **
|
||||||
|
|
||||||
function putAsync(url, args) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
client.put(url, args, (data, response) => {
|
|
||||||
if (response.statusCode >= 400) {
|
|
||||||
reject(data);
|
|
||||||
}
|
|
||||||
resolve(data);
|
|
||||||
}).on('error', e => reject(e));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function postAsync(url, args) {
|
async function postAsync(url, args) {
|
||||||
const fetch = require('node-fetch').default;
|
const fetch = require('node-fetch').default;
|
||||||
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
const response = await fetch(url, { method: 'POST', timeout: 0, ...args });
|
||||||
|
|
Loading…
Reference in New Issue