mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Compare commits
423 Commits
a5b53b0e75
...
1.12.10
Author | SHA1 | Date | |
---|---|---|---|
|
f246bc5ac6 | ||
|
2c5cffe6be | ||
|
f9e3666d23 | ||
|
72e0f8ccc5 | ||
|
e45d2252aa | ||
|
1d22cd7592 | ||
|
ac4a0e8af5 | ||
|
cf8bb8817b | ||
|
9e6db659d9 | ||
|
7c879a3abc | ||
|
e9c9dda3fb | ||
|
569f9a2091 | ||
|
8508c4bf9b | ||
|
4698b41d26 | ||
|
f47c98f85d | ||
|
48e5c11f9b | ||
|
8a55cd776f | ||
|
f43f057b07 | ||
|
6d0f58c862 | ||
|
b4c275f9e4 | ||
|
170378e615 | ||
|
97dc041777 | ||
|
ca62db2fbd | ||
|
5b4adbacba | ||
|
56da6eb39d | ||
|
75485f2d7d | ||
|
b4e5cfe797 | ||
|
9ad9877d3e | ||
|
227201a5fc | ||
|
d58f471cc3 | ||
|
d46a551d78 | ||
|
6432adaf3c | ||
|
8bf78ce9cb | ||
|
2109a7bda1 | ||
|
fba02d69fa | ||
|
7896e81986 | ||
|
6b587c1160 | ||
|
e5989e0118 | ||
|
e34d777e11 | ||
|
71fc73ce37 | ||
|
245b9384bc | ||
|
4ee0375120 | ||
|
d463677328 | ||
|
de6c8c1501 | ||
|
e0af5ff353 | ||
|
8447c7e637 | ||
|
f416b6cd54 | ||
|
e9260e8809 | ||
|
e92d49377f | ||
|
ca9d5c2fd8 | ||
|
a2355ab2ef | ||
|
7c40834eb7 | ||
|
b834b71c69 | ||
|
b4aadff649 | ||
|
7baf7da90d | ||
|
8a80b43c52 | ||
|
8454583131 | ||
|
5da312356a | ||
|
cfa4bcc1b8 | ||
|
421402615a | ||
|
7db2042b7b | ||
|
3a29756109 | ||
|
ec0f57a8eb | ||
|
ad19a800ce | ||
|
e1b6eab9eb | ||
|
2ca70090aa | ||
|
69f8d02c53 | ||
|
44833b665e | ||
|
e37a4b982a | ||
|
cdb31699d4 | ||
|
c9db7a1574 | ||
|
0ded442bd3 | ||
|
4c7d160d41 | ||
|
662f0e9c73 | ||
|
c3ad87cea1 | ||
|
d8b738d73e | ||
|
7edc6b4123 | ||
|
af21c92bd6 | ||
|
3596ba6d38 | ||
|
e7d3351c1d | ||
|
7a7d6a500d | ||
|
35913e2dfb | ||
|
57a7d549c0 | ||
|
149de7d257 | ||
|
6a76ebe747 | ||
|
aaf0f24d60 | ||
|
c4f119ebf9 | ||
|
39cfb35c1a | ||
|
77f5f00151 | ||
|
08baa1f9ae | ||
|
59772bfd27 | ||
|
a82c05a8ac | ||
|
6286eb5827 | ||
|
7885739a55 | ||
|
bba36e2145 | ||
|
90ff4c7308 | ||
|
29acc2e2db | ||
|
0d8977d688 | ||
|
d14f2f3c77 | ||
|
77414045d9 | ||
|
4fa8e0a89c | ||
|
c42a7739d6 | ||
|
b5eb2b5150 | ||
|
27f8768141 | ||
|
844d8bdee7 | ||
|
f92f8f4a84 | ||
|
4a4e02450c | ||
|
0d79bb6f6d | ||
|
e33e0b1e64 | ||
|
7f2cf6f7e6 | ||
|
0cb82b3288 | ||
|
28c7dbbe1b | ||
|
c89804677a | ||
|
0b43717931 | ||
|
540d93592b | ||
|
7adc6d38e2 | ||
|
09dd9762f7 | ||
|
7490357d9e | ||
|
1a16957519 | ||
|
83e677d6cb | ||
|
8394b97600 | ||
|
c4a92c95e6 | ||
|
650853eabb | ||
|
71050ef1d2 | ||
|
d166ccf455 | ||
|
5eff49f442 | ||
|
616fc34826 | ||
|
404a217622 | ||
|
7f94cb4bee | ||
|
f0b124221c | ||
|
3f7b91a4eb | ||
|
17df259afd | ||
|
964437ed13 | ||
|
6044aebe1f | ||
|
1ebaf18210 | ||
|
352a8e9a61 | ||
|
0f93caa427 | ||
|
e6107ad447 | ||
|
c3a12cc1a2 | ||
|
7bb37f129d | ||
|
78a1397a3c | ||
|
713443d234 | ||
|
d9101ce679 | ||
|
5739efc59b | ||
|
71d2530329 | ||
|
2c0895bd7d | ||
|
6033c38b19 | ||
|
107e2e0ed0 | ||
|
2796d5dcde | ||
|
ba7e34c195 | ||
|
c51e27fb69 | ||
|
f056e6a89c | ||
|
f794b7621f | ||
|
21ee072677 | ||
|
d07ee76784 | ||
|
252043ae11 | ||
|
85ce522270 | ||
|
2fbe689605 | ||
|
1f22b9c6bc | ||
|
222edd5c36 | ||
|
f649e4698b | ||
|
d27d750cb2 | ||
|
f1bc217e79 | ||
|
73614f2f8d | ||
|
d7328af4c8 | ||
|
94de9411b6 | ||
|
4232f6c5f4 | ||
|
1dd97c139e | ||
|
7e7b3e30c4 | ||
|
e83182c03b | ||
|
f3c12fb926 | ||
|
d5ed0f6cdf | ||
|
503c3ad22b | ||
|
6193eb14da | ||
|
372ac26080 | ||
|
00363cc206 | ||
|
dbc78c1c42 | ||
|
8753ae34be | ||
|
a6f37880e2 | ||
|
7eacdac665 | ||
|
0819588e7a | ||
|
0180e01c25 | ||
|
b094a355c0 | ||
|
2ef9f5d748 | ||
|
247a23bda9 | ||
|
0ea4494dea | ||
|
d8b1fd3b0b | ||
|
3f253f42f2 | ||
|
0e3b4335eb | ||
|
ce536201e6 | ||
|
12cc607754 | ||
|
266b181d49 | ||
|
2d66b7204a | ||
|
e49301308c | ||
|
4c3b987ee4 | ||
|
d16f5a24f4 | ||
|
43feffdfae | ||
|
9ea8fc92e4 | ||
|
9cab0618b6 | ||
|
58213d0ab0 | ||
|
954ed6c2b2 | ||
|
df5f69dc66 | ||
|
e762405cba | ||
|
6fce056b8c | ||
|
b8fc9f2194 | ||
|
713c05f808 | ||
|
e960ae64c5 | ||
|
8811010c73 | ||
|
e15e6dc3bd | ||
|
756f88b5aa | ||
|
5c1a9a3003 | ||
|
a7e8d00145 | ||
|
103251070f | ||
|
1ef25d6176 | ||
|
0e5100180b | ||
|
cd0b834291 | ||
|
f9eb720f2a | ||
|
6f4350b3a7 | ||
|
294b15976c | ||
|
3167019faf | ||
|
d1932f4cb5 | ||
|
e7b53dcb46 | ||
|
205f1d7adb | ||
|
47dea8159e | ||
|
79e3dda1df | ||
|
fa62326eeb | ||
|
10835de6e2 | ||
|
5c82ccf435 | ||
|
b94ed54283 | ||
|
f05ad26969 | ||
|
cf1b98e25d | ||
|
c4c9f43a60 | ||
|
67cb89f634 | ||
|
a64c8ade9d | ||
|
0090d68559 | ||
|
d9e0df0884 | ||
|
7b0d854fe6 | ||
|
9e8277882c | ||
|
5f8989437e | ||
|
f677b914f7 | ||
|
c05265d1a1 | ||
|
363c36da9c | ||
|
3698f40709 | ||
|
16c2183fd0 | ||
|
3be17e2ed8 | ||
|
00006aa072 | ||
|
62be0939d0 | ||
|
bc94ff7800 | ||
|
84bc14dfe5 | ||
|
0e81fb6a4a | ||
|
4bf58ca2d5 | ||
|
003d17b2c5 | ||
|
58ac746442 | ||
|
19c18d54f5 | ||
|
9c08f20e06 | ||
|
e773678f2d | ||
|
01dec6466c | ||
|
bcfb07de5e | ||
|
df8e0ba923 | ||
|
5a01eb8eb1 | ||
|
f5088b398f | ||
|
3c82d961bd | ||
|
c5b3ce0d5c | ||
|
050f485a5b | ||
|
2922f19a2a | ||
|
cc73a45d1f | ||
|
bcbfcb87b5 | ||
|
66f02f59c0 | ||
|
323b9407df | ||
|
3ed5d892f7 | ||
|
d2a39f7de8 | ||
|
b500406c2f | ||
|
099744b298 | ||
|
024213a8e4 | ||
|
c268713d84 | ||
|
1d0664a197 | ||
|
ece0cc9080 | ||
|
5166720f07 | ||
|
60caa67667 | ||
|
d9ee8aa3dc | ||
|
7a463708bd | ||
|
f98f83471a | ||
|
c842c6f238 | ||
|
9a13128398 | ||
|
210fac321b | ||
|
7098e17d22 | ||
|
e21931b6eb | ||
|
12db782a30 | ||
|
1d3abf54a5 | ||
|
1ecc65f5fe | ||
|
87535c6ec8 | ||
|
0638953a20 | ||
|
126616d539 | ||
|
83965fb611 | ||
|
c33649753b | ||
|
abe51682c8 | ||
|
84d2530c52 | ||
|
bea991b665 | ||
|
3849908fe1 | ||
|
bf4357774d | ||
|
54ca9477dd | ||
|
8574a5dd27 | ||
|
f6b9cd970d | ||
|
add108b821 | ||
|
a1115ab4ff | ||
|
b8a9a55246 | ||
|
51f89aaf7b | ||
|
0849cfca98 | ||
|
485e9e2eaa | ||
|
3ae858113b | ||
|
192a1f4014 | ||
|
0da4590597 | ||
|
3ed095bb0e | ||
|
22ea5f3c3c | ||
|
c78cb3b463 | ||
|
1a8e37d3a5 | ||
|
d5221760a3 | ||
|
dce63f52c0 | ||
|
d6f34f7b2c | ||
|
073b76a693 | ||
|
9c43999e4b | ||
|
8663c88ae8 | ||
|
7b732edf61 | ||
|
77841dbc21 | ||
|
3502bfcaa0 | ||
|
f5f60e269e | ||
|
7dfba69fc1 | ||
|
4cf5d1535e | ||
|
7f9766c918 | ||
|
e55748fe92 | ||
|
a137c23116 | ||
|
63f9f33e20 | ||
|
3dd1e25a3b | ||
|
23a10fdc22 | ||
|
373a0ad321 | ||
|
960db2d59b | ||
|
3d8f8d90c3 | ||
|
41a3035a2a | ||
|
a702dab68b | ||
|
efe25ddc04 | ||
|
a7c8b93652 | ||
|
23e59a1189 | ||
|
e9fc488661 | ||
|
2ef095c273 | ||
|
e6be28acea | ||
|
fb1f890974 | ||
|
8ef49b40b2 | ||
|
79700fd983 | ||
|
8de1d26eaa | ||
|
69da293fb9 | ||
|
cb21162558 | ||
|
bab9d3c74b | ||
|
9960db0ae2 | ||
|
4466da63bc | ||
|
7ce2841588 | ||
|
c3f988f246 | ||
|
80c8e83f09 | ||
|
86b4247767 | ||
|
36051fa5db | ||
|
980ebb2fbc | ||
|
1c803972cd | ||
|
82dff4f204 | ||
|
af582f43a6 | ||
|
b08cdffc64 | ||
|
ce34f14f19 | ||
|
ade6980037 | ||
|
de3f340a55 | ||
|
39c3924b3f | ||
|
8e94589f83 | ||
|
66a862f797 | ||
|
c930a66d81 | ||
|
89ec8fd23a | ||
|
d1a654f41f | ||
|
a86735d743 | ||
|
56c99000c4 | ||
|
4a7a11dfd5 | ||
|
8e20ebb534 | ||
|
04b68d2cce | ||
|
c3c16ea0d6 | ||
|
8b7a14f895 | ||
|
2f7bc7ca8d | ||
|
a444a782e2 | ||
|
0bebf02c97 | ||
|
7fbff41329 | ||
|
53514b5e1a | ||
|
c0b37631bc | ||
|
e124a22ffd | ||
|
095d19cda7 | ||
|
176ef77624 | ||
|
2384031d09 | ||
|
710a4ee867 | ||
|
3a1a955164 | ||
|
e8004b5b56 | ||
|
c873362d01 | ||
|
52606616c4 | ||
|
768b3e48f7 | ||
|
afccb8517a | ||
|
b5c2ecdfcc | ||
|
afb4acc19b | ||
|
f630c8892a | ||
|
4f24f8078d | ||
|
8bfb695536 | ||
|
ef35adb9e4 | ||
|
76c2789587 | ||
|
f4ef9697e9 | ||
|
309157dd89 | ||
|
1395c0b8c6 | ||
|
8d67bdee84 | ||
|
78c55558af | ||
|
dd4a1bf072 | ||
|
16ba8331b5 | ||
|
8dbd78f560 | ||
|
f730e2179b | ||
|
faf80d1b62 | ||
|
e02f57b7b0 | ||
|
4988f22e94 | ||
|
e56faaaed5 | ||
|
e1d6a47048 | ||
|
9382845dee | ||
|
d1cac298c6 | ||
|
e2c083ba31 | ||
|
2661881bc4 | ||
|
eb29f03ab0 |
@@ -12,3 +12,4 @@ access.log
|
||||
/data
|
||||
/cache
|
||||
.DS_Store
|
||||
/public/scripts/extensions/third-party
|
||||
|
@@ -54,6 +54,7 @@ module.exports = {
|
||||
},
|
||||
// These scripts are loaded in HTML; tell ESLint not to complain about them being undefined
|
||||
globals: {
|
||||
globalThis: 'readonly',
|
||||
ePub: 'readonly',
|
||||
pdfjsLib: 'readonly',
|
||||
toastr: 'readonly',
|
||||
@@ -90,6 +91,8 @@ module.exports = {
|
||||
'space-infix-ops': 'error',
|
||||
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }],
|
||||
'no-cond-assign': 'error',
|
||||
'no-unneeded-ternary': 'error',
|
||||
'no-irregular-whitespace': ['error', { skipStrings: true, skipTemplates: true }],
|
||||
|
||||
// These rules should eventually be enabled.
|
||||
'no-async-promise-executor': 'off',
|
||||
|
2
.github/readme-de_de.md
vendored
2
.github/readme-de_de.md
vendored
@@ -8,7 +8,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | German | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
|
||||
[English](readme.md) | German | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
|
2
.github/readme-ja_jp.md
vendored
2
.github/readme-ja_jp.md
vendored
@@ -5,7 +5,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md)
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | 日本語 | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
|
394
.github/readme-ko_kr.md
vendored
Normal file
394
.github/readme-ko_kr.md
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
> [!IMPORTANT]
|
||||
> 이곳에 게재된 정보는 오래되거나 불완전할 수 있습니다. 최신 정보는 영어 버전을 이용하십시오.
|
||||
|
||||
<a name="readme-top"></a>
|
||||
|
||||
![][cover]
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | 한국어
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
[](https://github.com/SillyTavern/SillyTavern/issues)
|
||||
[](https://github.com/SillyTavern/SillyTavern/pulls)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
SillyTavern은 많은 LLM API(KoboldAI/CPP, Horde, NovelAI, Ooba, Tabby, OpenAI, OpenRouter, Claude, Mistral 등)에 대한 단일 통합 인터페이스, 모바일 친화적 레이아웃, 비주얼 노벨 모드, Automatic1111 & ComfyUI API 이미지 생성 통합, TTS, 월드 인포 (로어북), 커스텀 가능한 UI, 자동 번역, 필요 이상의 프롬프트 옵션, 그리고 서드파티 확장을 통한 무궁무진한 성장 가능성을 제공합니다.
|
||||
|
||||
또한, 자주 묻는 질문에 대한 답변과, 시작하는 데 도움을 주기 위한 [문서 웹사이트](https://docs.sillytavern.app/)가 있습니다.
|
||||
|
||||
## SillyTavern이 무엇인가요?
|
||||
|
||||
SillyTavern(짧게는 ST)은 텍스트 생성 LLM, 이미지 생성 엔진, TTS 음성 모델 등과 상호작할 수 있는 로컬 설치형 UI 입니다.
|
||||
|
||||
2023년 2월, TavernAI 1.2.8의 포크로 시작한 SillyTavern은 현재 100명이 넘는 기여자를 보유하고 있으며, 2년간의 독자적인 개발을 거쳐 숙련된 AI 애호가들을 위한 선도적인 소프트웨어로 자리매김하고 있습니다.
|
||||
|
||||
|
||||
## 우리의 비전
|
||||
|
||||
1. 저희는 사용자가 LLM 프롬프트에 대한 최대한의 유용성과 제어 능력을 갖도록 하는 것을 목표로 합니다. 빠르게 배우는 것 역시 재미의 일부입니다!
|
||||
2. 저희는 어떠한 온라인 및 호스팅 서브시도 제공하지 않으며, 프로그래밍으로 사용자의 데이터를 추적하지 않습니다.
|
||||
3. SillyTavern은 헌신적인 LLM 커뮤니티가 여러분에게 제공하는 열정적인 프로젝트이며, 언제나 무료이며 오픈소스로 제공될 것입니다.
|
||||
|
||||
## 브랜치
|
||||
|
||||
SillyTavern은 모든 사용자가 원활한 경험을 할 수 있도록 두 개의 브랜치를 활용하여 개발되고 있습니다.
|
||||
|
||||
|
||||
* `release` -🌟 **대부분의 사용자에게 추천됨.** 가장 안정적이고 권장되는 브랜치이며, 주요 릴리스가 배포될 때만 업데이트됩니다. 대부분의 사용자에게 적합합니다. 일반적으로 한달에 한번 업데이트됩니다.
|
||||
* `staging` - ⚠️ **일반적인 사용에 추천되지 않음.** 최신 기능을 가지고 있지만, 언제든지 문제가 발생할 수 있습니다. 고급 사용자 및 숙련자 전용입니다. 하루에 여러번 업데이트됩니다.
|
||||
|
||||
만약 git CLI 사용에 익숙하지 않거나 브랜치가 무엇인지 모르겠다면 release 브랜치가 더 나은 선택입니다.
|
||||
|
||||
## SillyTavern 외에 무엇이 필요한가요?
|
||||
|
||||
SillyTavern은 인터페이스 역할만 하기 때문에, 실제로 채팅하려면 LLM 백엔드에 대한 액세스 권한이 필요합니다. 즉시 사용 가능한 채팅을 위해 AI Horde를 사용할 수 있습니다. 그 외에도 OpenAI 호환 API, KoboldAI, Tabby 등 많은 로컬 및 클라우드 기반 LLM 백엔드를 지원합니다. 지원되는 API에 대한 자세한 내용은 [FAQ](https://docs.sillytavern.app/usage/api-connections/)에서 확인할 수 있습니다.
|
||||
|
||||
### SillyTavern을 위해서 좋은 성능의 PC가 필요한가요?
|
||||
|
||||
하드웨어 요구 사항은 거의 없습니다: NodeJS 18 이상을 실행할 수 있는 모든 환경에서 작동합니다. 다만 로컬 LLM 모델을 사용할 경우, 최소 6GB VRAM 이상의 3000번대 NVIDIA 그래픽 카드를 권장합니다. 자세한 내용은 백엔드 문서를 참고하세요.
|
||||
|
||||
### 추천되는 백엔드 (제휴 없음)
|
||||
|
||||
* [AI Horde](https://aihorde.net/) - 자원 봉사자들이 호스팅하는 모델을 사용합니다. 추가 설정이 필요하지 않습니다.
|
||||
* [KoboldCpp](https://github.com/LostRuins/koboldcpp) - 로컬에서 GGUF 모델을 실행하기 위한 커뮤니티에서 선호하는 옵션입니다.
|
||||
* [tabbyAPI](https://github.com/theroyallab/tabbyAPI) - 인기 있는 경량 로컬 exl2 추론 API입니다.
|
||||
* [OpenRouter](https://openrouter.ai) - OpenAI, Claude, Meta Llama 등 다양한 클라우드 제공업체와 인기 있는 커뮤니티 모델을 위한 단일 API입니다.
|
||||
|
||||
## 질문이나 제안이 있으신가요?
|
||||
|
||||
### 디스코드 서버
|
||||
|
||||
| [![][discord-shield-badge]][discord-link] | [저희의 디스코드에 참여하세요!](https://discord.gg/sillytavern) 지원을 받고, 좋아하는 캐릭터와 프롬프트를 공유하세요. |
|
||||
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
혹은 저희의 개발자들과 직접 연락할 수 있습니다:
|
||||
|
||||
* 디스코드: cohee, rossascends, wolfsblvt
|
||||
* 레딧: [/u/RossAscends](https://www.reddit.com/user/RossAscends/), [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/)
|
||||
* [GitHub issue를 작성하세요](https://github.com/SillyTavern/SillyTavern/issues)
|
||||
|
||||
### 이 프로젝트가 마음에 들어요! 어떻게 기여할 수 있을까요?
|
||||
|
||||
1. PULL REQUEST를 생성하세요. 기여 방법에 대해서는 [CONTRIBUTING.md](../CONTRIBUTING.md)를 참고하세요.
|
||||
2. 제공된 탬플릿에 따라 기능 제안이나 이슈 리포트를 생성하세요.
|
||||
3. 중복된 이슈를 생성하지 않도록 이 README 파일 전체를 읽고 문서 웹사이트를 먼저 확인하세요.
|
||||
|
||||
## 스크린샷
|
||||
|
||||
<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f">
|
||||
<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1">
|
||||
|
||||
|
||||
## 캐릭터 카드
|
||||
|
||||
SillyTavern은 "캐릭터 카드"라는 개념을 중심으로 구축되었습니다. 캐릭터 카드는 LLM의 동작을 설정하는 프롬프트 모음이며, SillyTavern에서 지속적인 대화를 하려면 필수적입니다. 이는 ChatGPT의 GPT 또는 Poe의 봇과 유사하게 작동합니다. 캐릭터 카드의 내용은 추상적인 시나리오, 특정 작업에 맞춰진 도우미, 유명 인사 또는 가상 인물 등 무엇이든 될 수 있습니다.
|
||||
|
||||
이름 필드는 유일한 필수 캐릭터 카드 입력 항목입니다. 언어 모델과 중립적인 대화를 시작하려면 "도우미"라고 간단히 이름 지은 새 카드를 만들고 나머지 상자는 비워 두세요. 더 주제가 있는 채팅을 원한다면 언어 모델에 다양한 배경 정보, 행동 및 작문 패턴, 그리고 채팅을 바로 시작할 시나리오를 제공할 수 있습니다.
|
||||
|
||||
캐릭터 카드를 선택하지 않고 빠른 대화를 하거나 LLM 연결을 테스트하려면 SillyTavern을 연 후 시작 화면의 입력 창에 프롬프트 입력을 입력하기만 하면 됩니다. 이러한 채팅은 임시적이며 저장되지 않습니다.
|
||||
|
||||
캐릭터 카드를 정의하는 방법에 대한 일반적인 아이디어를 얻으려면 기본 캐릭터(Seraphina)를 보거나 "확장 프로그램 및 에셋 다운로드" 메뉴에서 선택된 커뮤니티 제작 카드를 다운로드하세요.
|
||||
|
||||
|
||||
## 핵심 기능
|
||||
|
||||
* 고급 텍스트 생성 설정과 다양한 커뮤니티 제작 프리셋
|
||||
* 월드 인포 지원: 풍부한 설정을 만들거나 캐릭터 카드에 토큰 저장
|
||||
* 그룹 채팅: 캐릭터가 사용자 혹은 다른 캐릭터와 대화할 수 있는 방
|
||||
* 다양한 UI 커스텀 옵션: 테마 색, 뱌경 이미지, 커스텀 CSS 등
|
||||
* 유저 페르소나: AI에게 사용자에 대한 정보를 주어 더욱 몰입감을 높임
|
||||
* 내장 RAG 지원: AI가 참조할 수 있도록 채팅에 문서를 추가
|
||||
* 광범위한 채팅 명령어 시스템 및 자체 [스크립트](https://docs.sillytavern.app/usage/st-script/)
|
||||
|
||||
## 확장
|
||||
|
||||
SillyTavern은 확장(익스텐션)을 지원합니다.
|
||||
|
||||
* 캐릭터 감정 표현 (스프라이트)
|
||||
* 채팅 기록 자동 요약
|
||||
* 자동 UI 및 채팅 번역
|
||||
* Stable Diffusion/FLUX/DALL-E 이미지 생성
|
||||
* AI 응답 메시지 텍스트 음성 변환 (ElevenLabs, Silero 또는 OS 시스템 TTS 사용)
|
||||
* 프롬프트에 추가적인 현실 세계 맥락을 추가하기 위한 웹 검색 기능
|
||||
* "확장 프로그램 및 에셋 다운로드" 메뉴에서 더 많은 기능을 다운로드할 수 있습니다.
|
||||
|
||||
|
||||
사용 방법에 대한 튜토리얼은 [Docs](https://docs.sillytavern.app/)에서 확인할 수 있습니다.
|
||||
|
||||
# ⌛ Installation
|
||||
|
||||
> \[!WARNING]
|
||||
>
|
||||
> * **윈도우 제어 폴더에는 설치하지 마십시오 (Program Files, System32 등).**
|
||||
> * **권리자 권한으로 START.BAT을 실행하지 마십시오.**
|
||||
> * **Windows 7에서는 NodeJS 18.16을 실행할 수 없으므로 설치가 불가능합니다.**
|
||||
|
||||
## 🪟 Windows
|
||||
|
||||
### Git을 통해 설치하기
|
||||
|
||||
1. [NodeJS](https://nodejs.org/ko) 설치 (최신 LTS 버전 권장)
|
||||
2. [Git for Windows](https://gitforwindows.org/) 설치
|
||||
3. 파일 탐색기 열기 (`Win+E`)
|
||||
4. Windows에서 제어하거나 모니터하지 않는 폴더를 찾거나 만드세요. (ex: C:\MySpecialFolder\)
|
||||
5. 상단의 주소 표시줄을 클릭하고 `cmd`를 입력한 후 Enter 키를 눌러 해당 폴더 내에서 명령 프롬프트를 여세요.
|
||||
6. 검은색 창(명령 프롬프트)이 나타나면 다음 중 하나를 입력하고 Enter 키를 누르세요.
|
||||
|
||||
* Release 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b release`
|
||||
* Staging 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
|
||||
|
||||
7. clone이 완료되면, `Start.bat`을 더블 클릭하여 NodeJS가 필요한 구성요소를 설치하도록 하세요.
|
||||
8. 그러면 서버가 시작하고, SillyTavern이 브라우저에 나타납니다.
|
||||
|
||||
### GitHub Desktop을 통해 설치하기
|
||||
|
||||
(이 방법은 **오직** GitHub Desktop에서만 git 사용이 가능합니다. 명령 프롬프트에서 git을 사용하려면 [Git for Windows](https://gitforwindows.org/)를 설치해야 합니다.)
|
||||
|
||||
|
||||
|
||||
1. [NodeJS](https://nodejs.org/ko) 설치 (최신 LTS 버전 권장)
|
||||
2. [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32) 설치
|
||||
3. GitHub Desktop을 설치했으면, `Clone a repository from the internet....`를 클릭하세요. (참고: 이 과정에서는 Github 계정이 **필요하지 않습니다**.)
|
||||
4. 메뉴에서 URL 탭을 클릭하고, 다음 URL을 입력한 후 복제를 클릭합니다: `https://github.com/SillyTavern/SillyTavern` 리포지토리가 다운로드될 위치를 변경하려면 로컬 경로를 변경할 수 있습니다.
|
||||
5. SillyTavern을 열려면 파일 탐색기를 사용하여 리포지토리를 복제한 폴더로 이동합니다. 기본적으로 리포지토리는 다음 위치에 복제됩니다: `C:\Users\[사용자 Windows 사용자 이름]\Documents\GitHub\SillyTavern`
|
||||
6. `start.bat` 파일을 더블 클릭 하세요. (참고: `.bat` 확장자 명은 OS 설정에 따라 보이지 않을 수 있습니다, 그럴 때는 파일 이름이 "`Start`" 처럼 보일 수 있습니다. 이 파일을 더블 클릭해 SillyTavern을 실행하세요.)
|
||||
7. 더블 클릭하면, 검고 큰 명령 프롬프트 창이 열리고 SillyTavern이 작동하는데 필요한 항목을 설치하기 시작합니다.
|
||||
8. 설치 과정이 끝나고 모든 것이 잘 작동한다면, 브라우저에 SillyTavern 탭이 열려 있어야 하고, 명령 프롬프트 창에 다음과 같이 표시되어야 합니다:
|
||||
9. Connect to any of the [supported APIs](https://docs.sillytavern.app/usage/api-connections/) and start chatting!
|
||||
|
||||
## 🐧 Linux & 🍎 MacOS
|
||||
|
||||
MacOS / Linux 에서는 이 모든 작업이 터미널에서 수행됩니다.
|
||||
|
||||
1. git과 nodeJS 설치 (이 작업은 OS에 따라 달라집니다.)
|
||||
2. 리포지토리 clone하기
|
||||
|
||||
* Release 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b release`
|
||||
* Staging 브랜치: `git clone https://github.com/SillyTavern/SillyTavern -b staging`
|
||||
|
||||
3. `cd SillyTavern` 를 입력해 설치 폴더로 이동하기
|
||||
4. `start.sh` 스크립트를 아래의 명령어 중 하나로 실행하기:
|
||||
|
||||
* `./start.sh`
|
||||
* `bash start.sh`
|
||||
|
||||
## ⚡ SillyTavern Launcher를 통해 설치하기
|
||||
|
||||
SillyTavern 런처는 로컬 LLM 사용을 위한 백엔드 설치를 포함하여 다양한 설정을 도와주는 설치 마법사입니다.
|
||||
|
||||
|
||||
### Windows 사용자
|
||||
|
||||
1. 키보드에서 **`WINDOWS + R`** 키를 눌러 실행 창을 여세요. 그리고 아래의 명령어를 입력해 git을 설치하세요.
|
||||
|
||||
```shell
|
||||
cmd /c winget install -e --id Git.Git
|
||||
```
|
||||
|
||||
2. 키보드에서 **`WINDOWS + E`** 키를 눌러 파일 탐색기를 열고 런처를 설치할 폴더로 이동합니다. 원하는 폴더에 도착하면 주소 표시줄에 `cmd`를 입력하고 Enter 키를 누릅니다. 그 후 아래의 명령어를 입력합니다.
|
||||
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
|
||||
```
|
||||
|
||||
### Linux 사용자
|
||||
|
||||
1. 선호하는 터미널을 열고 git을 설치하세요.
|
||||
2. SillyTavern-Launcher를 clone 하세요:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
|
||||
```
|
||||
|
||||
3. installer.sh를 실행하세요:
|
||||
|
||||
```shell
|
||||
chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
4. 설치가 끝나면 launcher.sh를 실행하세요:
|
||||
|
||||
```shell
|
||||
chmod +x launcher.sh && ./launcher.sh
|
||||
```
|
||||
|
||||
### Mac 사용자
|
||||
|
||||
1. 터미널을 열고 Brew를 설치하세요:
|
||||
|
||||
```shell
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
2. git을 설치하세요:
|
||||
|
||||
```shell
|
||||
brew install git
|
||||
```
|
||||
|
||||
3. SillyTavern-Launcher를 clone 하세요:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
|
||||
```
|
||||
|
||||
4. installer.sh를 실행하세요:
|
||||
|
||||
```shell
|
||||
chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
5. 설치가 끝나면 launcher.sh를 실행하세요:
|
||||
|
||||
```shell
|
||||
chmod +x launcher.sh && ./launcher.sh
|
||||
```
|
||||
|
||||
## 🐋 Docker를 통해 설치하기
|
||||
|
||||
이 방법은 Docker가 설치되어 있고, Docker 설치를 위해 커맨드 라인에 접근할 수 있으며, Docker의 일반적인 작동 방식에 익숙하다고 가정합니다.
|
||||
|
||||
### 이미지 직접 빌드하기
|
||||
|
||||
SillyTavern을 Docker에서 사용하는 방법에 대한 포괄적인 가이드는 [여기서](http://docs.sillytavern.app/installation/docker/) 확인할 수 있습니다. 이 가이드는 Windows, macOS 및 Linux에서의 설치를 다룹니다! 직접 이미지를 빌드하려면 이 가이드를 읽어보세요.
|
||||
|
||||
### GitHub Container Registry 사용하기 (가장 쉬움)
|
||||
|
||||
SillyTavern이 작동하려면 두 개의 필수 디렉터리 매핑과 하나의 포트 매핑이 필요합니다. 명령에서 다음 위치의 선택 항목을 바꿔주세요.
|
||||
|
||||
|
||||
#### Container Variables
|
||||
|
||||
##### Volume Mappings
|
||||
|
||||
* [config] - SillyTavern 구성 파일이 호스트 컴퓨터에 저장될 디렉터리
|
||||
* [data] - 캐릭터를 포함한 SillyTavern 사용자 데이터가 호스트 컴퓨터에 저장될 디렉터리
|
||||
* [plugins] - (선택 사항) SillyTavern 서버 플러그인이 호스트 컴퓨터에 저장될 디렉터리
|
||||
|
||||
##### Port Mappings
|
||||
|
||||
* [PublicPort] - 트래픽을 노출할 포트입니다. 가상 머신 컨테이너 외부에서 인스턴스에 접근하므로 필수 사항입니다. 보안을 위한 별도의 서비스를 구현하지 않고는 인터넷에 노출하지 마십시오.
|
||||
|
||||
|
||||
##### Additional Settings
|
||||
|
||||
* [DockerNet] - 컨테이너가 연결되어 생성되어야 하는 Docker 네트워크입니다. 해당 내용을 모르는 경우 [공식 Docker 문서](https://docs.docker.com/reference/cli/docker/network/)를 참조하세요.
|
||||
* [version] - 이 GitHub 페이지의 오른쪽에서 "Packages"를 선택하면 "sillytavern" 패키지를 볼 수 있습니다. "latest" 이미지 태그는 현재 릴리스와 함께 최신 상태를 유지합니다. 각 브랜치의 야간 이미지를 가리키는 "staging" 및 "release" 태그를 사용할 수도 있지만, 업데이트에 시간이 걸릴 수 있고 중단될 수 있는 확장 프로그램을 사용하는 경우에는 적합하지 않을 수 있습니다.
|
||||
|
||||
|
||||
#### 설치 명령어
|
||||
|
||||
1. 커맨드 라인 열기
|
||||
2. 아래의 명령어 실행
|
||||
|
||||
`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||
|
||||
> 8000은 기본 리스닝 포트입니다. 구성에서 포트를 변경한 경우 적절한 포트를 사용하는 것을 잊지 마세요.
|
||||
## 📱 Termux를 통해 Android OS에 설치하기
|
||||
|
||||
> \[!NOTE]
|
||||
> **SillyTavern은 Termux를 사용하여 Android 기기에서 기본적으로 실행할 수 있지만, 이러한 사용 사례에 대한 공식적인 지원은 제공하지 않습니다.**
|
||||
>
|
||||
> **ArroganceComplex#2659의 가이드를 참조하세요:**
|
||||
>
|
||||
> * <https://rentry.org/STAI-Termux>
|
||||
|
||||
**지원되지 않는 플랫폼: android arm LEtime-web.** 32비트 Android는 npm으로 설치할 수 없는 외부 종속성이 필요합니다. 다음 명령어를 사용하여 설치하세요: pkg install esbuild. 그런 다음 일반적인 설치 단계를 진행하세요.
|
||||
|
||||
|
||||
## API 키 관리
|
||||
|
||||
SillyTavern은 API 키를 사용자 데이터 디렉터리의 `secrets.json` 파일에 저장합니다 (`/data/default-user/secrets.json`이 기본 경로입니다).
|
||||
|
||||
|
||||
|
||||
기본적으로 API 키는 저장하고 페이지를 새로 고침한 후에는 인터페이스에서 보이지 않습니다.
|
||||
|
||||
키 보기 기능을 활성화하려면 다음 단계를 따르세요:
|
||||
|
||||
1. `config.yaml` 파일에서 `allowKeysExposure` 값을 `true로` 설정합니다.
|
||||
2. SillyTavern 서버를 다시 시작합니다.
|
||||
3. API 연결 패널 오른쪽 하단에 있는 '숨겨진 API 키 보기' 링크를 클릭합니다.
|
||||
|
||||
## 커맨드 라인 인수
|
||||
|
||||
`config.yaml`의 일부 설정을 덮어쓰기 위해 SillyTavern 서버 시작 시 커맨드 라인 인수를 전달할 수 있습니다.
|
||||
|
||||
|
||||
### 예시
|
||||
|
||||
```shell
|
||||
node server.js --port 8000 --listen false
|
||||
# 혹은
|
||||
npm run start -- --port 8000 --listen false
|
||||
# 혹은 (Windows 전용)
|
||||
Start.bat --port 8000 --listen false
|
||||
```
|
||||
|
||||
### 지원되는 인수
|
||||
|
||||
| 옵션 | 설명 | 타입 |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------------|----------|
|
||||
| `--version` | 버전 표시 | boolean |
|
||||
| `--enableIPv6` | IPv6 활성화 | boolean |
|
||||
| `--enableIPv4` | IPv4 활성화 | boolean |
|
||||
| `--port` | SillyTavern이 실행될 포트를 설정합니다. 설정되지 않은 경우 yaml config 'port'를 불러옵니다. | number |
|
||||
| `--dnsPreferIPv6` | DNS에 IPv6를 우선으로 할당합니다. 설정되지 않은 경우 yaml config를 불러옵니다. | boolean |
|
||||
| `--autorun` | 브라우저에서 SillyTavern을 자동으로 실행합니다. 설정되지 않은 경우 yaml config 'autorun'를 불러옵니다. | boolean |
|
||||
| `--autorunHostname` | 자동 실행 호스트 이름, 'auto'가 최적의 설정일 것입니다. | string |
|
||||
| `--autorunPortOverride` | 자동 실행 포트 덮어쓰기 | string |
|
||||
| `--listen` | SillyTavern이 모든 네트워크 인터페이스에서 수신 대기합니다. 설정되지 않은 경우 yaml 구성 'listen'을 불러옵니다. | boolean |
|
||||
| `--corsProxy` | CORS 프록시 활성화. 설정되지 않은 경우 yaml 구성 'enableCorsProxy'을 불러옵니다. | boolean |
|
||||
| `--disableCsrf` | CSRF 보호 비활성화 | boolean |
|
||||
| `--ssl` | SSL 활성화 | boolean |
|
||||
| `--certPath` | 인증서 파일 경로 | string |
|
||||
| `--keyPath` | 프라이빗 키 파일 경로 | string |
|
||||
| `--whitelist` | 화이트리스트 모드 활성화 | boolean |
|
||||
| `--dataRoot` | 데이터 스토리지의 루트 디렉토리 | string |
|
||||
| `--avoidLocalhost` | 자동 모드에서 자동 실행 시 'localhost' 사용 방지 | boolean |
|
||||
| `--basicAuthMode` | 기본 인증 활성화 | boolean |
|
||||
| `--requestProxyEnabled` | 외부 리퀘스트 프록시 활성화 | boolean |
|
||||
| `--requestProxyUrl` | 프록시 URL 리퀘스트 (HTTP 혹은 SOCKS 프로토콜) | string |
|
||||
| `--requestProxyBypass` | 프록시 바이패스 리스트 리퀘스트 (공백으로 구분된 호스트 목록) | array |
|
||||
|
||||
## 원격 연결
|
||||
|
||||
대부분의 경우 이는 PC에서 ST 서버를 실행하는 동안 모바일 장치에서 SillyTavern을 사용하려는 사람들을 위한 것입니다. 그러나 원격 연결을 다른 곳에서도 허용하도록 사용할 수 있습니다.
|
||||
|
||||
원격 연결 설정 방법에 대한 자세한 가이드는 [Docs](https://docs.sillytavern.app/usage/remoteconnections/)에서 확인할 수 있습니다.
|
||||
|
||||
또한 암호 보호 기능이 포함된 SillyTavern 사용자 프로필을 구성할 수 있습니다 (선택 사항): [Users](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users).
|
||||
|
||||
## 성능 이슈가 발생하나요?
|
||||
|
||||
1. 사용자 설정 패널(UI 테마 전환 카테고리)에서 흐림 효과를 비활성화하고 동작 줄이기를 활성화합니다.
|
||||
2. 응답 스트리밍을 사용하는 경우 스트리밍 FPS를 더 낮은 값(10-15 FPS 권장)으로 설정합니다.
|
||||
3. 브라우저에서 렌더링에 GPU 가속을 사용하도록 설정되어 있는지 확인합니다.
|
||||
|
||||
## 라이센스 및 크레딧
|
||||
|
||||
**이 프로그램은 유용할 것이라는 희망으로 배포되지만, 어떠한 보증도 제공하지 않습니다. 상품성 또는 특정 목적에의 적합성에 대한 묵시적인 보증조차도 제공하지 않습니다. 자세한 내용은 GNU Affero 일반 공중 사용 허가서를 참조하십시오.**
|
||||
|
||||
* Humi의 [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8: MIT 라이선스
|
||||
* CncAnon의 TavernAITurbo 모드의 일부는 허가를 받아 사용됨
|
||||
* PepperTaco의 작업(<https://github.com/peppertaco/Tavern/>)에 영감을 받은 비주얼 노벨 모드
|
||||
* Noto Sans Font by Google (OFL 라이선스)
|
||||
* Font Awesome의 아이콘 테마 <https://fontawesome.com> (아이콘: CC BY 4.0, 폰트: SIL OFL 1.1, 코드: MIT 라이선스)
|
||||
* 기본 콘텐츠는 @OtisAlejandro (Seraphina 캐릭터 및 로어북)와 @kallmeflocc (10K 디스코드 사용자 축전 배경화면)가 제공함
|
||||
* [@mrguymiah](https://github.com/mrguymiah)와 [@Bronya-Rand](https://github.com/Bronya-Rand)의 Docker 가이드
|
||||
|
||||
## 상위 기여자
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/graphs/contributors)
|
||||
|
||||
<!-- LINK GROUP -->
|
||||
[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44
|
||||
[discord-link]: https://discord.gg/sillytavern
|
||||
[discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge
|
40
.github/readme-ru_ru.md
vendored
40
.github/readme-ru_ru.md
vendored
@@ -7,7 +7,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | Русский | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
@@ -209,6 +209,44 @@ SillyTavern поддерживает расширения.
|
||||
5. Запустите лаунчер установки: `chmod +x install.sh && ./install.sh` and choose what you wanna install
|
||||
6. После завершения установки, запустите лаунчер следующей командой: `chmod +x launcher.sh && ./launcher.sh`
|
||||
|
||||
## 🐋 Установка с помощью Docker
|
||||
|
||||
Предполагается, что вы уже установили Docker, имеете доступ к командной строке для установки контейнеров и знакомы с их базовым управлением.
|
||||
|
||||
### Сборка образа самостоятельно
|
||||
|
||||
У нас есть подробное руководство по использованию SillyTavern в Docker [здесь](http://docs.sillytavern.app/installation/docker/), которое охватывает установку на Windows, macOS и Linux! Ознакомьтесь с ним, если хотите создать образ самостоятельно.
|
||||
|
||||
### Использование реестра контейнеров GitHub (самый простой способ)
|
||||
|
||||
Для работы SillyTavern вам понадобятся две обязательные настройки каталогов и одна настройка порта. В команде замените указанные значения на свои:
|
||||
|
||||
#### Переменные контейнера
|
||||
|
||||
##### Маппинг томов
|
||||
|
||||
* [config] - директория, где на вашем хосте будут храниться файлы конфигурации SillyTavern.
|
||||
* [data] - директория, где на вашем хосте будут храниться пользовательские данные SillyTavern (включая персонажей).
|
||||
* [plugins] - (необязательно) директория, где на вашем хосте будут храниться плагины сервера SillyTavern.
|
||||
|
||||
##### Маппинг портов
|
||||
|
||||
* [PublicPort] - Порт, через который будет передаваться трафик. Это обязательно, так как вы будете обращаться к контейнеру извне его виртуальной машины. НЕ ОТКРЫВАЙТЕ этот порт в интернет без реализации дополнительного уровня безопасности.
|
||||
|
||||
##### Дополнительные настройки
|
||||
|
||||
* [DockerNet] - Docker сеть, к которой контейнер должен быть подключен. Если вы не знаете, что это, обратитесь к [официальной документации Docker](https://docs.docker.com/reference/cli/docker/network/).
|
||||
* [version] - на правой части этой страницы GitHub вы найдете раздел "Packages". Выберите пакет "sillytavern", чтобы увидеть версии образов. Тег "latest" позволит вам обновляться до текущего релиза. Также доступны теги "staging" и "release", которые соответствуют ночным сборкам соответствующих веток. Однако это может быть нецелесообразно, если вы используете расширения, которые могут ломаться и требуют времени для обновления.
|
||||
|
||||
#### Команда установки
|
||||
|
||||
1. Откройте командную строку
|
||||
2. Выполните следующую команду
|
||||
|
||||
`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||
|
||||
> Заметьте, что 8000 является портом по умолчанию. Не забудьте использовать соответствующий порт, если вы измените его в конфиге.
|
||||
|
||||
## 📱 Мобильные устройства - Установка при помощи termux
|
||||
|
||||
> **ОБРАТИТЕ ВНИМАНИЕ!**
|
||||
|
2
.github/readme-zh_cn.md
vendored
2
.github/readme-zh_cn.md
vendored
@@ -5,7 +5,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | [German](readme-de_de.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
|
||||
[English](readme.md) | [German](readme-de_de.md) | 中文 | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
|
381
.github/readme-zh_tw.md
vendored
Normal file
381
.github/readme-zh_tw.md
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
> [!IMPORTANT]
|
||||
> 這裡的資訊可能已經過時或不完整,僅供您參考。請使用英文版本以取得最新資訊。
|
||||
|
||||
<a name="readme-top"></a>
|
||||
|
||||
![][cover]
|
||||
|
||||
<div align="center">
|
||||
|
||||
[English](readme.md) | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | 繁體中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
[](https://github.com/SillyTavern/SillyTavern/issues)
|
||||
[](https://github.com/SillyTavern/SillyTavern/pulls)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
SillyTavern 提供一個統一的前端介面,整合多種大型語言模型的 API(包括:KoboldAI/CPP、Horde、NovelAI、Ooba、Tabby、OpenAI、OpenRouter、Claude、Mistral 等)。同時具備行動裝置友善的佈局、視覺小說模式(Visual Novel Mode)、Automatic1111 與 ComfyUI 的影像生成 API 整合、TTS(語音合成)、世界資訊(Lorebook)、可自訂 UI、自動翻譯功能,以及強大的提示詞(prompt)設定選項和無限的第三方擴充潛力。
|
||||
|
||||
我們擁有一個 [官方文件網站](https://docs.sillytavern.app/) 可以幫助解答絕大多數的使用問題,並幫助您順利入門。
|
||||
|
||||
## SillyTavern 是什麼?
|
||||
|
||||
SillyTavern(簡稱 ST)是一款本地安裝的使用者介面,讓您能與大型語言模型(LLM)、影像生成引擎以及語音合成模型互動的前端。
|
||||
|
||||
SillyTavern 起源於 2023 年 2 月,作為 TavernAI 1.2.8 的分支版本發展至今。目前已有超過 100 位貢獻者,並擁有超過兩年的獨立開發歷史。如今,它已成為 AI 愛好者中備受推崇的軟體之一。
|
||||
|
||||
## 我們的願景
|
||||
|
||||
1. 我們致力於賦予使用者對 LLM 提示詞的最大控制權與實用性,並認為學習過程中的挑戰是樂趣的一部分
|
||||
2. 我們不提供任何線上或託管服務,也不會程式化追蹤任何使用者數據。
|
||||
3. SillyTavern 是由一群熱衷於 LLM 的開發者社群所打造的熱情專案,並將永遠保持免費與開源。
|
||||
|
||||
## 分支介紹
|
||||
|
||||
SillyTavern 採用雙分支開發模式,確保為所有使用者提供流暢的使用體驗。
|
||||
|
||||
* `release`(穩定版):🌟 **推薦給大部分的使用者使用。** 此分支最為穩定,僅在主要版本發布時更新。適合大多數人,通常每月更新一次。
|
||||
* `staging`(開發版):⚠️ **不建議普通使用者使用。** 此分支包含最新功能,但可能隨時出現問題。適合進階使用者與愛好者,每日多次更新。
|
||||
|
||||
如果您不熟悉 git CLI 或對分支概念不清楚,請放心對您來說,`release`(穩定版)分支永遠是首選。
|
||||
|
||||
## 使用 SillyTavern 需要什麼?
|
||||
|
||||
由於 SillyTavern 僅是一個介面,您需要一個 LLM 後端來提供推理能力。您可以使用 AI Horde 以立即開始聊天。此外,我們支持許多其他本地和雲端 LLM 後端,例如 OpenAI 兼容 API、KoboldAI、Tabby 等。更多支持的 API 資訊,請參閱 [常見問題](https://docs.sillytavern.app/usage/api-connections/)。
|
||||
|
||||
### 我需要高效能電腦才能運行 SillyTavern 嗎?
|
||||
|
||||
SillyTavern 的硬體需求相當低。任何能夠運行 NodeJS 18 或更高版本的設備都可以執行。若您打算在本地機器上進行 LLM 推理,我們建議使用擁有至少 6GB VRAM 的 3000 系列 NVIDIA 顯示卡。更多詳細資訊,請參考您使用的後端文檔。
|
||||
|
||||
### 推薦後端(僅為推薦,非官方合作和隸屬關係)
|
||||
|
||||
* [AI Horde](https://aihorde.net/):使用志願者託管的模型,無需進一步設定
|
||||
* [KoboldCpp](https://github.com/LostRuins/koboldcpp):社群推崇的選擇,可在本地運行 GGUF 模型
|
||||
* [tabbyAPI](https://github.com/theroyallab/tabbyAPI):一個流行且輕量的本地託管 exl2 推理 API
|
||||
* [OpenRouter](https://openrouter.ai):提供多個雲端 LLM 提供商(如 OpenAI、Claude、Meta Llama 等)及熱門社群模型的單一 API
|
||||
|
||||
## 有任何問題或建議?
|
||||
|
||||
### 歡迎加入我們的 Discord 伺服器
|
||||
|
||||
| [![][discord-shield-badge]][discord-link] | [加入我們的 Disocrd 伺服器](https://discord.gg/sillytavern) 以獲得技術支援、分享您喜愛的角色與提示詞。 |
|
||||
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
或直接聯繫開發者:
|
||||
|
||||
* Discord: cohee, rossascends, wolfsblvt
|
||||
* Reddit: [/u/RossAscends](https://www.reddit.com/user/RossAscends/), [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/)
|
||||
* [提交 GitHub 問題](https://github.com/SillyTavern/SillyTavern/issues)
|
||||
|
||||
### 我喜歡這個專案,我該如何貢獻呢?
|
||||
|
||||
1. **提交拉取要求(Pull Request)**:想了解如何貢獻,請參閱 [CONTRIBUTING.md](../CONTRIBUTING.md)。
|
||||
2. **提供功能建議與問題報告**:使用本專案所提供的模板提交建議或問題報告。
|
||||
3. **仔細閱讀此 README 文件及相關文檔**:請避免提出重複問題或建議。
|
||||
|
||||
## 螢幕截圖
|
||||
|
||||
<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f">
|
||||
<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1">
|
||||
|
||||
## 角色卡
|
||||
|
||||
SillyTavern 的核心概念是「角色卡」(Character Cards)。角色卡是一組設定 LLM 行為的提示詞,用於 SillyTavern 中進行持續性對話。其功能類似於 ChatGPT 的 GPT 或 Poe 的聊天機器人。角色卡的內容可以是任何形式:抽象場景、針對特定任務設計的助手、知名人物,或者虛構角色。
|
||||
|
||||
角色卡中唯一必填的項目是名稱欄位。若想與語言模型開始一般對話,您只需創建一個名稱為「Assistant」的新卡片,其餘欄位皆可保持空白。若希望進行更具主題性的對話,則可以提供語言模型背景資訊、行為模式、寫作風格以及特定情境來啟動聊天。
|
||||
|
||||
如果您僅想進行快速對話而不選擇角色卡片,或想測試 LLM 的連線,則可在打開 SillyTavern 後,於歡迎頁面的輸入欄位中直接輸入您的提示內容。請注意,這類對話是暫時的,不會被永久保存。
|
||||
|
||||
若想了解如何設定角色卡,可參考預設角色(如 Seraphina)或從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載社群製作的角色卡。
|
||||
|
||||
## 核心功能
|
||||
|
||||
* 進階文本生成設定:內含許多社群製作的預設設定
|
||||
* 支援世界資訊(World Info):創建豐富的背景故事,或節省角色卡片中的 Token(符記)使用
|
||||
* 群組聊天:多角色聊天室,可讓角色與您或彼此對話
|
||||
* 豐富的 UI 自定義選項:主題顏色、背景圖片、自定義 CSS 等
|
||||
* 使用者設定:讓 AI 更了解您並提升沉浸感
|
||||
* 內建 RAG 支持:可將文檔加入對話,供 AI 參考
|
||||
* 強大的聊天指令子系統:內含 [腳本引擎(Scripting Engine)](https://docs.sillytavern.app/usage/st-script/)
|
||||
|
||||
## 擴充功能
|
||||
|
||||
SillyTavern 支持多種擴充功能。
|
||||
|
||||
* 角色情感表達:使用視覺圖片(立繪)呈現情緒表達
|
||||
* 聊天記錄自動摘要
|
||||
* 自動化介面與聊天翻譯
|
||||
* 穩定擴散(Stable Diffusion)、FLUX 和 DALL-E 的影像生成整合
|
||||
* 語音合成:AI 回應訊息可透過 ElevenLabs、Silero 或系統 TTS 語音合成
|
||||
* 網頁搜尋功能:為提示詞添加真實世界的上下文資訊
|
||||
* 更多擴展:可從「下載擴充功能 & 資源」(Download Extensions & Assets)選單中下載
|
||||
|
||||
想了解如何使用這些擴充功能,請參考:[官方說明文件](https://docs.sillytavern.app/)
|
||||
|
||||
# ⌛ 安裝指南
|
||||
|
||||
> \[!WARNING]
|
||||
>
|
||||
> * 請勿將程式安裝到 Windows 的系統控制資料夾(如 Program Files、System32 等)
|
||||
> * 請勿以管理員權限執行 Start.bat
|
||||
> * 無法在 Windows 7 系統上安裝,因為它無法執行 NodeJS 18.16
|
||||
|
||||
## 🪟 Windows
|
||||
|
||||
### 使用 Git 安裝
|
||||
|
||||
1. 安裝 [NodeJS](https://nodejs.org/en)(建議使用最新的 LTS 版本)
|
||||
2. 安裝 [Git for Windows](https://gitforwindows.org/)
|
||||
3. 打開 Windows 檔案總管(`Win+E`)
|
||||
4. 創建/使用一個不受 Windows 系統控制或監控的資料夾(例如:C:\MySpecialFolder\)
|
||||
5. 在該資料夾內開啟命令提示字元(Command Prompt):點擊地址欄,輸入 `cmd` 並按下 Enter
|
||||
6. 當命令提示字元黑框彈出時,輸入以下其中一條指令後,按下 Enter:
|
||||
|
||||
* 安裝 Release(穩定版)分支:`git clone https://github.com/SillyTavern/SillyTavern -b release`
|
||||
* 安裝 Staging(開發板)分支:`git clone https://github.com/SillyTavern/SillyTavern -b staging`
|
||||
|
||||
7. 當程式碼下載完成後,雙擊 `Start.bat`,NodeJS 將自動安裝所需的依賴項
|
||||
8. 本地伺服器啟動後,SillyTavern 將自動在您的瀏覽器中打開
|
||||
|
||||
### 使用 GitHub Desktop 安裝
|
||||
|
||||
(此方式僅允許通過 GitHub Desktop 使用 git。如果您也希望在命令列中使用 `git`,則需額外安裝 [Git for Windows](https://gitforwindows.org/))
|
||||
|
||||
1. 安裝 [NodeJS](https://nodejs.org/en)(建議使用最新的 LTS 版本)
|
||||
2. 安裝 [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32)
|
||||
3. 安裝完成後,打開 GitHub Desktop,點擊 `Clone a repository from the internet....` (注意:此步驟 **無需創建 GitHub 帳號**。)
|
||||
4. 在彈出選單中,點擊「URL」選項,輸入此網址:`https://github.com/SillyTavern/SillyTavern`,然後點擊「Clone」。您可以更改「Local path」來選擇 SillyTavern 的下載位置
|
||||
6. 若想開啟 SillyTavern,需使用 Windows 檔案總管以進入您複製儲存庫的資料夾。預設位置為:`C:\Users\[您的 Windows 使用者名稱]\Documents\GitHub\SillyTavern`
|
||||
7. 雙擊 `start.bat` 文件。(請注意:若您的作業系統隱藏了 `.bat` 副檔名,該文件可能顯示為「`Start`」。這就是您需要雙擊運行的文件。)
|
||||
8. 雙擊後,將會彈出一個大型黑色的命令提示字元視窗,SillyTavern 會開始安裝其運行所需的文件與依賴
|
||||
9. 安裝完成後,若一切正常,命令提示字元視窗應顯示運行中的訊息,且您的瀏覽器會自動打開 SillyTavern 頁籤
|
||||
10. 連接到任何 SillyTavern [支援的 APIs](https://docs.sillytavern.app/usage/api-connections/) 並開始聊天吧!
|
||||
|
||||
## 🐧 Linux & 🍎 MacOS
|
||||
|
||||
對於 MacOS 和 Linux 系統,所有操作都將在終端機(Terminal)中完成。
|
||||
|
||||
1. 安裝 git 和 NodeJS(具體方法因操作系統而異)
|
||||
2. 複製儲存庫(Clone the repo):
|
||||
|
||||
* 安裝 Release(穩定版)分支:`git clone https://github.com/SillyTavern/SillyTavern -b release`
|
||||
* 安裝 Staging(開發板)分支:`git clone https://github.com/SillyTavern/SillyTavern -b staging`
|
||||
|
||||
3. 使用命令 `cd SillyTavern` 以進入安裝資料夾
|
||||
4. 使用以下其中一條命令,以執行 `start.sh` 腳本:
|
||||
|
||||
* `./start.sh`
|
||||
* `bash start.sh`
|
||||
|
||||
## ⚡ 使用 SillyTavern Launcher 安裝
|
||||
|
||||
SillyTavern Launcher 是一個安裝嚮導,協助您設定多種選項,包括安裝本地推理(inference)的後端。
|
||||
|
||||
### 對於 Windows 使用者
|
||||
|
||||
1. 在鍵盤上按下 **`WINDOWS + R`** 打開「執行」對話框,然後輸入以下指令以安裝 git:
|
||||
|
||||
```shell
|
||||
cmd /c winget install -e --id Git.Git
|
||||
```
|
||||
|
||||
2. 在鍵盤上按下 **`WINDOWS + E`** 打開檔案總管,導航至您想要安裝 Launcher 的資料夾。在目標資料夾的地址欄輸入 `cmd` 並按下 Enter。接著執行以下命令:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat
|
||||
```
|
||||
|
||||
### 對於 Linux 使用者
|
||||
|
||||
1. 打開您喜歡的終端機(Terminal),安裝 git
|
||||
2. 使用以下指令以複製 Sillytavern-Launcher:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
|
||||
```
|
||||
|
||||
3. 執行安裝腳本(installer.sh):
|
||||
|
||||
```shell
|
||||
chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
4. 安裝完成後,執行啟動腳本(launcher.sh):
|
||||
|
||||
```shell
|
||||
chmod +x launcher.sh && ./launcher.sh
|
||||
```
|
||||
|
||||
### 對於 Mac 使用者
|
||||
|
||||
1. 打開終端機(Terminal),並使用以下指令安裝 Homebrew:
|
||||
|
||||
```shell
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
2. 使用 Homebrew 以安裝 git:
|
||||
|
||||
```shell
|
||||
brew install git
|
||||
```
|
||||
|
||||
3. 使用以下指令以複製 Sillytavern-Launcher:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher
|
||||
```
|
||||
|
||||
4. 執行安裝腳本(installer.sh):
|
||||
|
||||
```shell
|
||||
chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
5. 安裝完成後,執行啟動腳本(launcher.sh):
|
||||
|
||||
```shell
|
||||
chmod +x launcher.sh && ./launcher.sh
|
||||
```
|
||||
|
||||
## 🐋 使用 Docker 安裝
|
||||
|
||||
以下指南已假設您安裝 Docker,能夠訪問命令列進行容器安裝,並熟悉 Docker 的基本使用。
|
||||
|
||||
### 自行構建映像
|
||||
|
||||
我們提供了一份完整的 [SillyTavern Docker 使用指南](http://docs.sillytavern.app/installation/docker/)。該指南涵蓋了 Windows、macOS 和 Linux 的安裝過程。若您希望自行構建映像,建議先閱讀該文檔。
|
||||
|
||||
### 使用 GitHub 容器註冊表(最簡易的方式)
|
||||
|
||||
您需要設定兩個必要的目錄映射(directory mappings)和一個端口映射(port mapping)來使 SillyTavern 正常運行。在執行指令時,請將以下佔位符替換為您的實際配置:
|
||||
|
||||
#### 容器變數
|
||||
|
||||
##### 目錄映射(Volume Mappings)
|
||||
|
||||
* [config]:用於存放 SillyTavern 設定文件的本地資料夾
|
||||
* [data]:用於存放 SillyTavern 使用者數據(包括角色)的本地資料夾
|
||||
* [plugins](可選):用於存放 SillyTavern 擴充功能的本地資料夾
|
||||
|
||||
##### 端口映射(Port Mappings)
|
||||
|
||||
* [PublicPort]:對外流量的訪問端口。這是必需的,因為您將從虛擬機容器外部訪問實例。除非實施了額外的安全服務,否則請勿將此端口暴露於網路
|
||||
|
||||
##### 其他設定(Additional Settings)
|
||||
|
||||
* [DockerNet]:容器應連接的 Docker 網路。如果您不熟悉此概念,請參閱 [Docker 官方說明文件](https://docs.docker.com/reference/cli/docker/network/)
|
||||
* [version]:在 GitHub 頁面的右側,您可以找到「Packages」。選擇「sillytavern」包,然後查看映像版本。「latest」標籤會使您保持與當前版本同步。您也可以選擇「staging」或「release」標籤,但這可能不適用於依賴擴充功能的使用者,因為擴充功能可能需要時間進行更新
|
||||
|
||||
#### 安裝命令
|
||||
|
||||
1. 打開命令列(Command Line)
|
||||
2. 執行以下指令:
|
||||
|
||||
`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
|
||||
|
||||
> 請注意:默認的監聽端口為 8000。如果您在設定文件中更改了此端口,請務必使用適當的端口號
|
||||
|
||||
## 📱 於 Android 系統中使用 Termux 安裝
|
||||
|
||||
> \[!NOTE]
|
||||
> **雖然您可以在 Android 設備上使用 Termux 直接運行 SillyTavern,但這不在我們的官方支持範圍內。**
|
||||
>
|
||||
> **請參閱 ArroganceComplex#2659 所提供的指南:**
|
||||
>
|
||||
> * <https://rentry.org/STAI-Termux>
|
||||
|
||||
**不支援:Android ARM LEtime-web。** 32 位 Android 系統需要額外的依賴項,這無法通過 npm 安裝。請使用以下命令安裝:`pkg install esbuild`。完成後,請按照普通的安裝步驟進行操作
|
||||
|
||||
## API 金鑰管理
|
||||
|
||||
SillyTavern 將您的 API 金鑰(Keys)保存在使用者數據目錄中的 `secrets.json` 文件內(默認路徑為`/data/default-user/secrets.json`)
|
||||
|
||||
默認情況下,API 金鑰在您保存並重新載入頁面後,將不會自介面中顯示
|
||||
|
||||
如需啟用查看金鑰功能:
|
||||
|
||||
1. 在 `config.yaml` 文件中,將 `allowKeysExposure` 的「值」設為 `true`
|
||||
2. 重新啟動 SillyTavern 伺服器
|
||||
3. 點擊 API 連線頁面右下角的「查看隱藏的 API 金鑰(View hidden API keys)」超連結
|
||||
|
||||
## 命令列參數(Command-line Arguments)
|
||||
|
||||
您可以在啟動 SillyTavern 伺服器時傳遞命令列參數,以覆蓋 `config.yaml` 文件中的某些設定。
|
||||
|
||||
### 範例
|
||||
|
||||
```shell
|
||||
node server.js --port 8000 --listen false
|
||||
# or
|
||||
npm run start -- --port 8000 --listen false
|
||||
# or(僅適用於 Windows)
|
||||
Start.bat --port 8000 --listen false
|
||||
```
|
||||
|
||||
### Supported arguments
|
||||
|
||||
| Option | Description | Type |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------------|----------|
|
||||
| `--version` | 顯示版本序號 | boolean |
|
||||
| `--enableIPv6` | 啟用 IPv6 | boolean |
|
||||
| `--enableIPv4` | 啟用 IPv4 | boolean |
|
||||
| `--port` | 設定 SillyTavern 運行的端口。若未提供,則預設使用 `config.yaml` 中的 'port' | number
|
||||
| `--dnsPreferIPv6` | 偏好使用 IPv6 解析 DNS。未提供則默認使用 `config.yaml` 中的 'preferIPv6' | boolean |
|
||||
| `--autorun` | 自動在瀏覽器中啟動 SillyTavern。未提供則默認使用 `config.yaml` 中的 'autorun' | boolean |
|
||||
| `--autorunHostname` | 自動啟動時的主機名稱,通常建議保持為 'auto' | string |
|
||||
| `--autorunPortOverride` | 覆蓋自動啟動的端口設定 | string |
|
||||
| `--listen` | SillyTavern 是否可監聽所有網路接口。若未提供,則默認使用 `config.yaml` 中的 'listen' | boolean |
|
||||
| `--corsProxy` | 啟用 CORS 代理。若未提供,則默認使用 `config.yaml` 中的 'enableCorsProxy' | boolean |
|
||||
| `--disableCsrf` | 停用 CSRF 保護 | boolean |
|
||||
| `--ssl` | 啟用 SSL | boolean |
|
||||
| `--certPath` | 設定您證書文件的路徑 | string |
|
||||
| `--keyPath` | 設定您私人金鑰文件的路徑 | string |
|
||||
| `--whitelist` | 啟用白名單模式 | boolean |
|
||||
| `--dataRoot` | 設定數據儲存的根目錄 | string |
|
||||
| `--avoidLocalhost` | 在自動模式下避免使用 'localhost' | boolean |
|
||||
| `--basicAuthMode` | 啟用基本身份驗證模式 | boolean |
|
||||
| `--requestProxyEnabled` | 啟用代理以處理外部請求 | boolean |
|
||||
| `--requestProxyUrl` | 設定請求代理的 URL(支持 HTTP 或 SOCKS 協議) | string |
|
||||
| `--requestProxyBypass` | 請求代理的例外主機清單(主機列表需以空格分隔) | array |
|
||||
|
||||
## 遠端連線
|
||||
|
||||
遠端連線功能最常用於希望在手機上使用 SillyTavern 的使用者。此時伺服器將由同一 Wi-Fi 網路上的 PC 運行。不過,您也可以設定來自其他網路的遠端連線。
|
||||
|
||||
詳細設定指南請參閱 [官方說明文件](https://docs.sillytavern.app/usage/remoteconnections/)。
|
||||
|
||||
您還可以選擇設定 SillyTavern 的使用者檔案,並開啟密碼保護(可選):[使用者設定指南](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users)。
|
||||
|
||||
## 遇到任何效能問題?
|
||||
|
||||
1. 在「使用者設定」選單(設定介面主題)中,禁用模糊效果(Blur Effect),並開啟「減少動畫效果」(Reduced Motion)
|
||||
2. 若使用響應串流傳輸,請將串流的 FPS 設定為較低的值(建議設定為 10-15 FPS)
|
||||
3. 確保瀏覽器已啟用 GPU 加速以進行渲染
|
||||
|
||||
## 授權與致謝
|
||||
|
||||
**本程式(SillyTavern)的發布是基於其可能對使用者有所幫助的期許,但不提供任何形式的保證;包括但不限於對可銷售性(marketability)或特定用途適用性的隱含保證。如需更多詳情,請參閱 GNU Affero 通用公共許可證。**
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
* [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8 由 Humi 提供:MIT 許可
|
||||
* 經授權使用部分來自 CncAnon 的 TavernAITurbo 模組
|
||||
* 視覺小說模式(Visual Novel Mode)的靈感,來源於 PepperTaco 的貢獻(<https://github.com/peppertaco/Tavern/>)
|
||||
* Noto Sans 字體由 Google 提供(OFL 許可)
|
||||
* 主題圖示由 Font Awesome <https://fontawesome.com> 提供(圖示:CC BY 4.0,字體:SIL OFL 1.1,代碼:MIT 許可)
|
||||
* 預設資源來源於 @OtisAlejandro(包含角色 Seraphina 與知識書)與 @kallmeflocc(SillyTavern 官方 Discord 伺服器成員突破 10K 的慶祝背景)
|
||||
* Docker 安裝指南由 [@mrguymiah](https://github.com/mrguymiah) 和 [@Bronya-Rand](https://github.com/Bronya-Rand) 編寫
|
||||
|
||||
## 主要貢獻者
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/graphs/contributors)
|
||||
|
||||
<!-- LINK GROUP -->
|
||||
[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44
|
||||
[discord-link]: https://discord.gg/sillytavern
|
||||
[discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge
|
2
.github/readme.md
vendored
2
.github/readme.md
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md)
|
||||
English | [German](readme-de_de.md) | [中文](readme-zh_cn.md) | [繁體中文](readme-zh_tw.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | [한국어](readme-ko_kr.md)
|
||||
|
||||
[](https://github.com/SillyTavern/SillyTavern/stargazers)
|
||||
[](https://github.com/SillyTavern/SillyTavern/network)
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -50,3 +50,5 @@ public/css/user.css
|
||||
/default/scaffold
|
||||
public/scripts/extensions/third-party
|
||||
/certs
|
||||
.aider*
|
||||
.env
|
||||
|
@@ -11,3 +11,4 @@ access.log
|
||||
.github
|
||||
.vscode
|
||||
.git
|
||||
/public/scripts/extensions/third-party
|
||||
|
@@ -6,9 +6,6 @@ ARG APP_HOME=/home/node/app
|
||||
# Install system dependencies
|
||||
RUN apk add gcompat tini git
|
||||
|
||||
# Ensure proper handling of kernel signals
|
||||
ENTRYPOINT [ "tini", "--" ]
|
||||
|
||||
# Create app directory
|
||||
WORKDIR ${APP_HOME}
|
||||
|
||||
@@ -42,4 +39,5 @@ RUN \
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD [ "./docker-entrypoint.sh" ]
|
||||
# Ensure proper handling of kernel signals
|
||||
ENTRYPOINT ["tini", "--", "./docker-entrypoint.sh"]
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# -- DATA CONFIGURATION --
|
||||
# Root directory for user data storage
|
||||
dataRoot: ./data
|
||||
# The maximum amount of memory that parsed character cards can use in MB
|
||||
cardsCacheCapacity: 100
|
||||
# -- SERVER CONFIGURATION --
|
||||
# Listen for incoming connections
|
||||
listen: false
|
||||
@@ -96,6 +98,8 @@ skipContentCheck: false
|
||||
disableChatBackup: false
|
||||
# Number of backups to keep for each chat and settings file
|
||||
numberOfBackups: 50
|
||||
# Maximum number of chat backups to keep per user (starting from the most recent). Set to -1 to keep all backups.
|
||||
maxTotalChatBackups: -1
|
||||
# Interval in milliseconds to throttle chat backups per user
|
||||
chatBackupThrottleInterval: 10000
|
||||
# Allowed hosts for card downloads
|
||||
|
@@ -627,6 +627,14 @@
|
||||
"filename": "presets/instruct/Synthia.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Tulu.json",
|
||||
"type": "instruct"
|
||||
},
|
||||
{
|
||||
"filename": "presets/context/Tulu.json",
|
||||
"type": "context"
|
||||
},
|
||||
{
|
||||
"filename": "presets/instruct/Vicuna 1.0.json",
|
||||
"type": "instruct"
|
||||
|
11
default/content/presets/context/Tulu.json
Normal file
11
default/content/presets/context/Tulu.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"story_string": "<|system|>\n{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{char}}'s personality: {{personality}}\n{{/if}}{{#if scenario}}Scenario: {{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}\n",
|
||||
"example_separator": "",
|
||||
"chat_start": "",
|
||||
"use_stop_strings": false,
|
||||
"allow_jailbreak": false,
|
||||
"always_force_name2": true,
|
||||
"trim_sentences": false,
|
||||
"single_line": false,
|
||||
"name": "Tulu"
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"input_sequence": "<|im_start|>[{{name}}]",
|
||||
"output_sequence": "<|im_start|>[{{name}}]",
|
||||
"input_sequence": "<|im_start|>{{name}}",
|
||||
"output_sequence": "<|im_start|>{{name}}",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence": "<|im_start|>system",
|
||||
"stop_sequence": "<|im_end|>",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
|
||||
"output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n",
|
||||
"input_sequence": "<|start_header_id|>{{name}}<|end_header_id|>\n\n",
|
||||
"output_sequence": "<|start_header_id|>{{name}}<|end_header_id|>\n\n",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence": "<|start_header_id|>system<|end_header_id|>\n\n",
|
||||
"stop_sequence": "<|eot_id|>",
|
||||
|
22
default/content/presets/instruct/Tulu.json
Normal file
22
default/content/presets/instruct/Tulu.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"input_sequence": "<|user|>\n",
|
||||
"output_sequence": "<|assistant|>\n",
|
||||
"first_output_sequence": "",
|
||||
"last_output_sequence": "",
|
||||
"system_sequence_prefix": "",
|
||||
"system_sequence_suffix": "",
|
||||
"stop_sequence": "<|end_of_text|>",
|
||||
"wrap": false,
|
||||
"macro": true,
|
||||
"names_behavior": "always",
|
||||
"activation_regex": "",
|
||||
"skip_examples": false,
|
||||
"output_suffix": "<|end_of_text|>\n",
|
||||
"input_suffix": "\n",
|
||||
"system_sequence": "<|system|>\n",
|
||||
"system_suffix": "\n",
|
||||
"user_alignment_message": "",
|
||||
"last_system_sequence": "",
|
||||
"system_same_as_user": false,
|
||||
"name": "Tulu"
|
||||
}
|
@@ -39,8 +39,8 @@
|
||||
"proxy_password": "",
|
||||
"max_context_unlocked": false,
|
||||
"wi_format": "{0}",
|
||||
"scenario_format": "[Circumstances and context of the dialogue: {{scenario}}]",
|
||||
"personality_format": "[{{char}}'s personality: {{personality}}]",
|
||||
"scenario_format": "{{scenario}}",
|
||||
"personality_format": "{{personality}}",
|
||||
"group_nudge_prompt": "[Write the next reply only as {{char}}.]",
|
||||
"stream_openai": true,
|
||||
"prompts": [
|
||||
@@ -230,7 +230,6 @@
|
||||
"show_external_models": false,
|
||||
"assistant_prefill": "",
|
||||
"assistant_impersonation": "",
|
||||
"human_sysprompt_message": "Let's get started. Please generate your response based on the information and instructions provided above.",
|
||||
"claude_use_sysprompt": false,
|
||||
"use_alt_scale": false,
|
||||
"squash_system_messages": false,
|
||||
|
@@ -599,18 +599,22 @@
|
||||
"Default (none)": [],
|
||||
"Anti-bond": [
|
||||
{
|
||||
"id": "22154f79-dd98-41bc-8e34-87015d6a0eaf",
|
||||
"text": " bond",
|
||||
"value": -50
|
||||
},
|
||||
{
|
||||
"id": "8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f",
|
||||
"text": " future",
|
||||
"value": -50
|
||||
},
|
||||
{
|
||||
"id": "52a4b280-0956-4940-ac52-4111f83e4046",
|
||||
"text": " bonding",
|
||||
"value": -50
|
||||
},
|
||||
{
|
||||
"id": "e63037c7-c9d1-4724-ab2d-7756008b433b",
|
||||
"text": " connection",
|
||||
"value": -25
|
||||
}
|
||||
|
@@ -6,4 +6,4 @@ if [ ! -e "config/config.yaml" ]; then
|
||||
fi
|
||||
|
||||
# Start the server
|
||||
exec node server.js --listen
|
||||
exec node server.js --listen "$@"
|
||||
|
344
package-lock.json
generated
344
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sillytavern",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.10",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zeldafan0225/ai_horde": "^5.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"bing-translate-api": "^2.9.1",
|
||||
"bing-translate-api": "^4.0.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"bowser": "^2.11.0",
|
||||
"command-exists": "^1.2.9",
|
||||
@@ -63,11 +63,12 @@
|
||||
"showdown": "^2.1.0",
|
||||
"sillytavern-transformers": "2.14.6",
|
||||
"simple-git": "^3.19.1",
|
||||
"slidetoggle": "^4.0.0",
|
||||
"tiktoken": "^1.0.16",
|
||||
"url-join": "^5.0.0",
|
||||
"vectra": "^0.2.2",
|
||||
"wavefile": "^11.0.0",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-dev-middleware": "^7.4.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.17.1",
|
||||
"yaml": "^2.3.4",
|
||||
@@ -87,6 +88,9 @@
|
||||
"@types/deno": "^2.0.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jquery": "^3.5.29",
|
||||
"@types/jquery-cropper": "^1.0.4",
|
||||
"@types/jquery.transit": "^0.9.33",
|
||||
"@types/jqueryui": "^1.12.23",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/multer": "^1.4.12",
|
||||
@@ -96,6 +100,7 @@
|
||||
"@types/png-chunks-encode": "^1.0.2",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/response-time": "^2.3.8",
|
||||
"@types/select2": "^4.0.63",
|
||||
"@types/toastr": "^2.1.43",
|
||||
"@types/write-file-atomic": "^4.0.3",
|
||||
"@types/yargs": "^17.0.33",
|
||||
@@ -866,60 +871,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/json-pack": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz",
|
||||
"integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jsonjoy.com/base64": "^1.1.1",
|
||||
"@jsonjoy.com/util": "^1.1.2",
|
||||
"hyperdyperid": "^1.2.0",
|
||||
"thingies": "^1.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/util": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz",
|
||||
"integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
@@ -1281,6 +1232,36 @@
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jquery-cropper": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery-cropper/-/jquery-cropper-1.0.4.tgz",
|
||||
"integrity": "sha512-YMyUoY+rhB8yc3xM1B/daNaSq5+q93rzvRx6HP8K9mmvXEviTH3/rldlYNCGd0TmE/kLlZYJsruYhu9wY350PA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jquery": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jquery.transit": {
|
||||
"version": "0.9.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery.transit/-/jquery.transit-0.9.33.tgz",
|
||||
"integrity": "sha512-gEDi1Lw7qfHFxtcnm2dg0F3Z5yG+84Sn0gDpGbd+u+r2RxsCcdQzfUmFKzHGBjWflZ9CXOZiAkenKOSvwLITrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jquery": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jqueryui": {
|
||||
"version": "1.12.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.23.tgz",
|
||||
"integrity": "sha512-pm1yVNVI29B9IGw41anCEzA5eR2r1pYc7flqD471ZT7B0yUXIY7YNe/zq7LGpihIGXNzWyG+Q4YQSzv2AF3fNA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jquery": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@@ -1432,6 +1413,16 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/select2": {
|
||||
"version": "4.0.63",
|
||||
"resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.63.tgz",
|
||||
"integrity": "sha512-/DXUfPSj3iVTGlRYRYPCFKKSogAGP/j+Z0fIMXbBiBtmmZj0WH7vnfNuckafq9C43KnqPPQW2TI/Rj/vTSGnQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jquery": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
@@ -1803,45 +1794,6 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
@@ -2209,9 +2161,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bing-translate-api": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz",
|
||||
"integrity": "sha512-DaYqa7iupfj+fj/KeaeZSp5FUY/ZR4sZ6jqoTP0RHkYOUfo7wwoxlhYDkh4VcvBBzuVORnBEgdXBVQrfM4kk7g==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-4.0.2.tgz",
|
||||
"integrity": "sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"got": "^11.8.6"
|
||||
@@ -2602,12 +2554,6 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -3691,9 +3637,9 @@
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
@@ -3715,7 +3661,7 @@
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
@@ -3730,6 +3676,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
@@ -3785,12 +3735,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
|
||||
"integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
@@ -4541,15 +4485,6 @@
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hyperdyperid": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
|
||||
"integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.18"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@@ -5176,25 +5111,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz",
|
||||
"integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jsonjoy.com/json-pack": "^1.0.3",
|
||||
"@jsonjoy.com/util": "^1.3.0",
|
||||
"tree-dump": "^1.0.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
@@ -5859,9 +5775,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/peek-readable": {
|
||||
@@ -6310,15 +6226,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
@@ -6447,59 +6354,6 @@
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
|
||||
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"ajv-keywords": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv-keywords": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
@@ -6723,6 +6577,12 @@
|
||||
"integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/slidetoggle": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz",
|
||||
"integrity": "sha512-6qvrOS1dnDFEr41UEEFFRQE8nswaAFIYZAHer6dVlznRIjHyCISjNJoxIn5U5QlAbZfBBxTELQk4jS7miHto1A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
@@ -7038,18 +6898,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/thingies": {
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
|
||||
"integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
|
||||
"license": "Unlicense",
|
||||
"engines": {
|
||||
"node": ">=10.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/tiktoken": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.16.tgz",
|
||||
@@ -7106,22 +6954,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-dump": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz",
|
||||
"integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/streamich"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/truncate-utf8-bytes": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
@@ -7266,6 +7098,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
|
||||
"integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf8-byte-length": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||
@@ -7425,35 +7266,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz",
|
||||
"integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
"memfs": "^4.6.0",
|
||||
"mime-types": "^2.1.31",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"schema-utils": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
|
11
package.json
11
package.json
@@ -8,7 +8,7 @@
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zeldafan0225/ai_horde": "^5.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"bing-translate-api": "^2.9.1",
|
||||
"bing-translate-api": "^4.0.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"bowser": "^2.11.0",
|
||||
"command-exists": "^1.2.9",
|
||||
@@ -53,11 +53,12 @@
|
||||
"showdown": "^2.1.0",
|
||||
"sillytavern-transformers": "2.14.6",
|
||||
"simple-git": "^3.19.1",
|
||||
"slidetoggle": "^4.0.0",
|
||||
"tiktoken": "^1.0.16",
|
||||
"url-join": "^5.0.0",
|
||||
"vectra": "^0.2.2",
|
||||
"wavefile": "^11.0.0",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-dev-middleware": "^7.4.2",
|
||||
"write-file-atomic": "^5.0.1",
|
||||
"ws": "^8.17.1",
|
||||
"yaml": "^2.3.4",
|
||||
@@ -85,7 +86,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/SillyTavern/SillyTavern.git"
|
||||
},
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.10",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start:deno": "deno run --allow-run --allow-net --allow-read --allow-write --allow-sys --allow-env server.js",
|
||||
@@ -115,6 +116,9 @@
|
||||
"@types/deno": "^2.0.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/jquery": "^3.5.29",
|
||||
"@types/jquery-cropper": "^1.0.4",
|
||||
"@types/jquery.transit": "^0.9.33",
|
||||
"@types/jqueryui": "^1.12.23",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/multer": "^1.4.12",
|
||||
@@ -124,6 +128,7 @@
|
||||
"@types/png-chunks-encode": "^1.0.2",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/response-time": "^2.3.8",
|
||||
"@types/select2": "^4.0.63",
|
||||
"@types/toastr": "^2.1.43",
|
||||
"@types/write-file-atomic": "^4.0.3",
|
||||
"@types/yargs": "^17.0.33",
|
||||
|
@@ -65,7 +65,7 @@ label[for="extensions_autoconnect"] {
|
||||
}
|
||||
|
||||
.extensions_info .extension_enabled {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.extensions_info .extension_disabled {
|
||||
@@ -76,13 +76,44 @@ label[for="extensions_autoconnect"] {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
input.extension_missing[type="checkbox"] {
|
||||
opacity: 0.5;
|
||||
.extensions_info .extension_modules {
|
||||
font-size: 0.8em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#extensions_list .disabled {
|
||||
text-decoration: line-through;
|
||||
color: lightgray;
|
||||
.extensions_info .extension_block {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 5px;
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
border-radius: 10px;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.extensions_info .extension_name {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.extensions_info .extension_version {
|
||||
opacity: 0.8;
|
||||
font-size: 0.8em;
|
||||
font-weight: normal;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.extensions_info .extension_block a {
|
||||
color: var(--SmartThemeBodyColor);
|
||||
}
|
||||
|
||||
.extensions_info .extension_name.update_available {
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
input.extension_missing[type="checkbox"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.update-button {
|
||||
@@ -105,3 +136,13 @@ input.extension_missing[type="checkbox"] {
|
||||
#extensionsMenu>div.extension_container:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.extensions_info .extension_text_block {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.extensions_info .extension_actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
@@ -42,3 +42,9 @@ body.login .userSelect .userHandle {
|
||||
body.login .userSelect:hover {
|
||||
background-color: var(--black30a);
|
||||
}
|
||||
|
||||
body.login #handleEntryBlock,
|
||||
body.login #passwordEntryBlock,
|
||||
body.login #passwordRecoveryBlock {
|
||||
margin: 2px;
|
||||
}
|
||||
|
@@ -9,3 +9,11 @@ body.safari .popup .popup-body {
|
||||
max-height: 90vh;
|
||||
max-height: 90dvh;
|
||||
}
|
||||
|
||||
body.safari #select_chat_div {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body.safari #select_chat_popup {
|
||||
height: max-content;
|
||||
}
|
||||
|
@@ -28,6 +28,11 @@
|
||||
color: var(--white50a);
|
||||
}
|
||||
|
||||
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid[data-role] {
|
||||
vertical-align: unset;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt_invisible {
|
||||
display: none;
|
||||
}
|
||||
@@ -260,7 +265,7 @@
|
||||
}
|
||||
|
||||
#completion_prompt_manager #completion_prompt_manager_list .completion_prompt_manager_prompt .completion_prompt_manager_prompt_name .fa-solid.prompt-manager-overridden {
|
||||
margin-left: 5px;
|
||||
margin-left: 3px;
|
||||
color: var(--SmartThemeQuoteColor);
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
|
411
public/global.d.ts
vendored
411
public/global.d.ts
vendored
@@ -1,4 +1,5 @@
|
||||
import libs from './lib';
|
||||
import getContext from './scripts/st-context';
|
||||
|
||||
// Global namespace modules
|
||||
declare var ai;
|
||||
@@ -6,7 +7,7 @@ declare var pdfjsLib;
|
||||
declare var ePub;
|
||||
|
||||
declare var SillyTavern: {
|
||||
getContext(): any;
|
||||
getContext(): typeof getContext;
|
||||
llm: any;
|
||||
libs: typeof libs;
|
||||
};
|
||||
@@ -19,400 +20,24 @@ declare global {
|
||||
pagination(method: 'getCurrentPageNum'): number;
|
||||
pagination(method: string, options?: any): JQuery;
|
||||
pagination(options?: any): JQuery;
|
||||
transition(options?: any, complete?: function): JQuery;
|
||||
autocomplete(options?: any): JQuery;
|
||||
autocomplete(method: string, options?: any): JQuery;
|
||||
slider(options?: any): JQuery;
|
||||
slider(method: string, func: string, options?: any): JQuery;
|
||||
cropper(options?: any): JQuery;
|
||||
izoomify(options?: any): JQuery;
|
||||
}
|
||||
|
||||
//#region select2
|
||||
namespace Select2 {
|
||||
interface Options<Result = DataFormat | GroupedDataFormat, RemoteResult = any> {
|
||||
/**
|
||||
* Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field
|
||||
* [Custom Field]
|
||||
* @default ''
|
||||
*/
|
||||
searchInputPlaceholder?: string;
|
||||
|
||||
/**
|
||||
* Initializes or modifies a select2 instance with provided options
|
||||
*
|
||||
* @param options - Configuration options for the select2 instance
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
select2(options?: Select2Options): JQuery;
|
||||
|
||||
/**
|
||||
* Retrieves data currently selected in the select2 instance
|
||||
*
|
||||
* @param field - A string specifying the 'data' method
|
||||
* @returns An array of selected items
|
||||
*/
|
||||
select2(field: 'data'): any[];
|
||||
|
||||
/**
|
||||
* Calls the specified select2 method
|
||||
*
|
||||
* @param method - The name of the select2 method to invoke
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
select2(method: 'open' | 'close' | 'destroy' | 'focus' | 'val', value?: any): JQuery;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region sortable
|
||||
|
||||
/**
|
||||
* Initializes or updates a sortable instance with the provided options
|
||||
*
|
||||
* @param options - Configuration options for the sortable instance
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
sortable(options?: SortableOptions): JQuery;
|
||||
|
||||
/**
|
||||
* Calls a sortable method to perform actions on the instance
|
||||
*
|
||||
* @param method - The name of the sortable method to invoke
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
sortable(method: 'destroy' | 'disable' | 'enable' | 'refresh' | 'toArray'): JQuery;
|
||||
|
||||
/**
|
||||
* Retrieves the sortable's instance object. If the element does not have an associated instance, undefined is returned.
|
||||
*
|
||||
* @returns The instance of the sortable object
|
||||
*/
|
||||
sortable(method: 'instance'): object;
|
||||
|
||||
/**
|
||||
* Retrieves the current option value for the specified option
|
||||
*
|
||||
* @param method - The string 'option' to retrieve an option value
|
||||
* @param optionName - The name of the option to retrieve
|
||||
* @returns The value of the specified option
|
||||
*/
|
||||
sortable(method: 'option', optionName: string): any;
|
||||
|
||||
/**
|
||||
* Sets the value of the specified option
|
||||
*
|
||||
* @param method - The string 'option' to set an option value
|
||||
* @param optionName - The name of the option to set
|
||||
* @param value - The value to assign to the option
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
sortable(method: 'option', optionName: string, value: any): JQuery;
|
||||
|
||||
/**
|
||||
* Sets multiple options using an object
|
||||
*
|
||||
* @param method - The string 'option' to set options
|
||||
* @param options - An object containing multiple option key-value pairs
|
||||
* @returns The jQuery object for chaining
|
||||
*/
|
||||
sortable(method: 'option', options: SortableOptions): JQuery;
|
||||
|
||||
//#endregion
|
||||
/**
|
||||
* Extends select2 plugin by adding a custom css class for the 'search' input field
|
||||
* [Custom Field]
|
||||
* @default ''
|
||||
*/
|
||||
searchInputCssClass?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#region select2
|
||||
|
||||
/** Options for configuring a select2 instance */
|
||||
interface Select2Options {
|
||||
/**
|
||||
* Provides support for ajax data sources
|
||||
* @param params - Parameters including the search term
|
||||
* @param callback - A callback function to handle the results
|
||||
* @default null
|
||||
*/
|
||||
ajax?: {
|
||||
url: string;
|
||||
dataType?: string;
|
||||
delay?: number;
|
||||
data?: (params: any) => any;
|
||||
processResults?: (data: any, params: any) => any;
|
||||
} | { transport: (params, success, failure) => any };
|
||||
|
||||
/**
|
||||
* Provides support for clearable selections
|
||||
* @default false
|
||||
*/
|
||||
allowClear?: boolean;
|
||||
|
||||
/**
|
||||
* See Using Select2 with AMD or CommonJS loaders
|
||||
* @default './i18n/'
|
||||
*/
|
||||
amdLanguageBase?: string;
|
||||
|
||||
/**
|
||||
* Controls whether the dropdown is closed after a selection is made
|
||||
* @default true
|
||||
*/
|
||||
closeOnSelect?: boolean;
|
||||
|
||||
/**
|
||||
* Allows rendering dropdown options from an array
|
||||
* @default null
|
||||
*/
|
||||
data?: object[];
|
||||
|
||||
/**
|
||||
* Used to override the built-in DataAdapter
|
||||
* @default SelectAdapter
|
||||
*/
|
||||
dataAdapter?: SelectAdapter;
|
||||
|
||||
/**
|
||||
* Enable debugging messages in the browser console
|
||||
* @default false
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Sets the dir attribute on the selection and dropdown containers to indicate the direction of the text
|
||||
* @default 'ltr'
|
||||
*/
|
||||
dir?: string;
|
||||
|
||||
/**
|
||||
* When set to true, the select control will be disabled
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Used to override the built-in DropdownAdapter
|
||||
* @default DropdownAdapter
|
||||
*/
|
||||
dropdownAdapter?: DropdownAdapter;
|
||||
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
dropdownAutoWidth?: boolean;
|
||||
|
||||
/**
|
||||
* Adds additional CSS classes to the dropdown container. The helper :all: can be used to add all CSS classes present on the original <select> element.
|
||||
* @default ''
|
||||
*/
|
||||
dropdownCssClass?: string;
|
||||
|
||||
/**
|
||||
* Allows you to customize placement of the dropdown
|
||||
* @default $(document.body)
|
||||
*/
|
||||
dropdownParent?: JQuery | HTMLElement;
|
||||
|
||||
/**
|
||||
* Handles automatic escaping of content rendered by custom templates
|
||||
* @default Utils.escapeMarkup
|
||||
*/
|
||||
escapeMarkup?: function;
|
||||
|
||||
/**
|
||||
* Specify the language used for Select2 messages
|
||||
* @default EnglishTranslation
|
||||
*/
|
||||
language?: string | object;
|
||||
|
||||
/**
|
||||
* Handles custom search matching
|
||||
* @default null
|
||||
*/
|
||||
matcher?: (searchParams: object, data: object) => boolean;
|
||||
|
||||
/**
|
||||
* Maximum number of characters that may be provided for a search term
|
||||
* @default 0
|
||||
*/
|
||||
maximumInputLength?: number;
|
||||
|
||||
/**
|
||||
* The maximum number of items that may be selected in a multi-select control. If the value of this option is less than 1, the number of selected items will not be limited.
|
||||
* @default 0
|
||||
*/
|
||||
maximumSelectionLength?: number;
|
||||
|
||||
/**
|
||||
* Minimum number of characters required to start a search
|
||||
* @default 0
|
||||
*/
|
||||
minimumInputLength?: number;
|
||||
|
||||
/**
|
||||
* The minimum number of results required to display the search box
|
||||
* @default 0
|
||||
*/
|
||||
minimumResultsForSearch?: number;
|
||||
|
||||
/**
|
||||
* This option enables multi-select (pillbox) mode. Select2 will automatically map the value of the multiple HTML attribute to this option during initialization.
|
||||
* @default false
|
||||
*/
|
||||
multiple?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies the placeholder for the control
|
||||
* @default null
|
||||
*/
|
||||
placeholder?: string;
|
||||
|
||||
/**
|
||||
* Used to override the built-in ResultsAdapter
|
||||
* @default ResultsAdapter
|
||||
*/
|
||||
resultsAdapter?: ResultsAdapter;
|
||||
|
||||
/**
|
||||
* Used to override the built-in SelectionAdapter
|
||||
* @default SingleSelection | MultipleSelection
|
||||
*/
|
||||
selectionAdapter?: SingleSelection | MultipleSelection;
|
||||
|
||||
/**
|
||||
* Adds additional CSS classes to the selection container. The helper :all: can be used to add all CSS classes present on the original <select> element
|
||||
* @default ''
|
||||
*/
|
||||
selectionCssClass?: string;
|
||||
|
||||
/**
|
||||
* Implements automatic selection when the dropdown is closed
|
||||
* @default false
|
||||
*/
|
||||
selectOnClose?: boolean;
|
||||
|
||||
sorter?: function;
|
||||
|
||||
/**
|
||||
* When set to `true`, allows the user to create new tags that aren't pre-populated
|
||||
* Used to enable free text responses
|
||||
* @default false
|
||||
*/
|
||||
tags?: boolean | object[];
|
||||
|
||||
/**
|
||||
* Customizes the way that search results are rendered
|
||||
* @param item - The item object to format
|
||||
* @returns The formatted representation
|
||||
* @default null
|
||||
*/
|
||||
templateResult?: (item: any) => JQuery | string;
|
||||
|
||||
/**
|
||||
* Customizes the way that selections are rendered
|
||||
* @param item - The selected item object to format
|
||||
* @returns The formatted representation
|
||||
* @default null
|
||||
*/
|
||||
templateSelection?: (item: any) => JQuery | string;
|
||||
|
||||
/**
|
||||
* Allows you to set the theme
|
||||
* @default 'default'
|
||||
*/
|
||||
theme?: string;
|
||||
|
||||
/**
|
||||
* A callback that handles automatic tokenization of free-text entry
|
||||
* @default null
|
||||
*/
|
||||
tokenizer?: (input: { _type: string, term: string }, selection: { options: object }, callback: (Select2Option) => any) => { term: string };
|
||||
|
||||
/**
|
||||
* The list of characters that should be used as token separators
|
||||
* @default null
|
||||
*/
|
||||
tokenSeparators?: string[];
|
||||
|
||||
/**
|
||||
* Supports customization of the container width
|
||||
* @default 'resolve'
|
||||
*/
|
||||
width?: string;
|
||||
|
||||
/**
|
||||
* If true, resolves issue for multiselects using closeOnSelect: false that caused the list of results to scroll to the first selection after each select/unselect
|
||||
* @default false
|
||||
*/
|
||||
scrollAfterSelect?: boolean;
|
||||
|
||||
/**
|
||||
* Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field
|
||||
* [Custom Field]
|
||||
* @default ''
|
||||
*/
|
||||
searchInputPlaceholder?: string;
|
||||
|
||||
/**
|
||||
* Extends select2 plugin by adding a custom css class for the 'searcH' input field
|
||||
* [Custom Field]
|
||||
* @default ''
|
||||
*/
|
||||
searchInputCssClass?: string;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region sortable
|
||||
|
||||
/** Options for configuring a sortable instance */
|
||||
interface SortableOptions {
|
||||
/**
|
||||
* When set, prevents the sortable items from being dragged unless clicked with a delay
|
||||
* @default 0
|
||||
*/
|
||||
delay?: number;
|
||||
|
||||
/**
|
||||
* Class name for elements to handle sorting. Elements with this class can be dragged to sort.
|
||||
* @default ''
|
||||
*/
|
||||
handle?: string;
|
||||
|
||||
/**
|
||||
* Whether to allow sorting between different connected lists
|
||||
* @default false
|
||||
*/
|
||||
connectWith?: string | boolean;
|
||||
|
||||
/**
|
||||
* Function called when sorting starts
|
||||
* @param event - The event object
|
||||
* @param ui - The UI object containing the helper and position information
|
||||
*/
|
||||
start?: (event: Event, ui: SortableUI) => void;
|
||||
|
||||
/**
|
||||
* Function called when sorting stops
|
||||
* @param event - The event object
|
||||
* @param ui - The UI object containing the helper and position information
|
||||
*/
|
||||
stop?: (event: Event, ui: SortableUI) => void;
|
||||
|
||||
/**
|
||||
* Function called when sorting updates
|
||||
* @param event - The event object
|
||||
* @param ui - The UI object containing the helper and position information
|
||||
*/
|
||||
update?: (event: Event, ui: SortableUI) => void;
|
||||
|
||||
/**
|
||||
* Specifies which items inside the element should be sortable
|
||||
* @default '> *'
|
||||
*/
|
||||
items?: string;
|
||||
}
|
||||
|
||||
/** UI object passed to sortable event handlers */
|
||||
interface SortableUI {
|
||||
/** jQuery object representing the helper element */
|
||||
helper: JQuery;
|
||||
/** The current position of the helper element */
|
||||
position: { top: number; left: number };
|
||||
/** Original position of the helper element */
|
||||
originalPosition: { top: number; left: number };
|
||||
/** jQuery object representing the item being sorted */
|
||||
item: JQuery;
|
||||
/** The placeholder element used during sorting */
|
||||
placeholder: JQuery;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
6
public/img/deepseek.svg
Normal file
6
public/img/deepseek.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128px" height="128px" viewBox="0 0 128 128" version="1.1">
|
||||
<g id="surface1">
|
||||
<path d="M 126.65625 23.902344 C 125.300781 23.242188 124.714844 24.507812 123.925781 25.152344 C 123.652344 25.359375 123.425781 25.632812 123.195312 25.878906 C 121.210938 27.996094 118.894531 29.382812 115.871094 29.214844 C 111.449219 28.96875 107.675781 30.355469 104.335938 33.738281 C 103.625 29.566406 101.269531 27.082031 97.683594 25.484375 C 95.808594 24.652344 93.910156 23.824219 92.59375 22.015625 C 91.675781 20.730469 91.425781 19.296875 90.964844 17.886719 C 90.671875 17.035156 90.378906 16.164062 89.402344 16.019531 C 88.335938 15.855469 87.921875 16.746094 87.503906 17.492188 C 85.835938 20.542969 85.1875 23.902344 85.253906 27.308594 C 85.398438 34.964844 88.628906 41.066406 95.054688 45.402344 C 95.785156 45.898438 95.972656 46.398438 95.742188 47.125 C 95.308594 48.617188 94.785156 50.070312 94.324219 51.566406 C 94.03125 52.523438 93.59375 52.726562 92.570312 52.316406 C 89.109375 50.828125 85.96875 48.691406 83.3125 46.019531 C 78.742188 41.605469 74.613281 36.730469 69.460938 32.910156 C 68.269531 32.03125 67.042969 31.191406 65.785156 30.398438 C 60.535156 25.296875 66.480469 21.105469 67.855469 20.609375 C 69.296875 20.085938 68.351562 18.304688 63.703125 18.324219 C 59.050781 18.347656 54.792969 19.898438 49.371094 21.972656 C 48.566406 22.28125 47.734375 22.527344 46.890625 22.703125 C 41.820312 21.75 36.636719 21.566406 31.515625 22.160156 C 21.460938 23.28125 13.433594 28.039062 7.53125 36.148438 C 0.4375 45.898438 -1.230469 56.980469 0.8125 68.535156 C 2.960938 80.714844 9.179688 90.800781 18.730469 98.683594 C 28.640625 106.859375 40.046875 110.863281 53.066406 110.097656 C 60.96875 109.644531 69.777344 108.582031 79.703125 100.175781 C 82.207031 101.425781 84.832031 101.921875 89.195312 102.292969 C 92.554688 102.609375 95.785156 102.132812 98.289062 101.609375 C 102.207031 100.777344 101.9375 97.148438 100.523438 96.484375 C 89.03125 91.128906 91.550781 93.3125 89.253906 91.546875 C 95.097656 84.632812 103.898438 77.457031 107.34375 54.199219 C 107.609375 52.347656 107.382812 51.183594 107.34375 49.691406 C 107.324219 48.785156 107.53125 48.425781 108.570312 48.324219 C 111.457031 48.027344 114.253906 47.164062 116.8125 45.792969 C 124.257812 41.722656 127.265625 35.046875 127.972656 27.035156 C 128.078125 25.808594 127.953125 24.542969 126.65625 23.898438 Z M 61.765625 96 C 50.625 87.242188 45.222656 84.355469 42.992188 84.480469 C 40.902344 84.609375 41.28125 86.992188 41.738281 88.550781 C 42.21875 90.085938 42.84375 91.140625 43.71875 92.492188 C 44.324219 93.382812 44.742188 94.710938 43.113281 95.707031 C 39.523438 97.925781 33.289062 94.960938 32.996094 94.816406 C 25.738281 90.539062 19.664062 84.894531 15.390625 77.179688 C 11.265625 69.75 8.863281 61.78125 8.46875 53.273438 C 8.363281 51.214844 8.964844 50.492188 11.011719 50.117188 C 13.703125 49.601562 16.457031 49.53125 19.167969 49.910156 C 30.539062 51.574219 40.214844 56.65625 48.332031 64.703125 C 52.960938 69.289062 56.464844 74.769531 60.074219 80.121094 C 63.914062 85.808594 68.042969 91.226562 73.300781 95.664062 C 75.15625 97.222656 76.632812 98.40625 78.054688 99.273438 C 73.777344 99.753906 66.640625 99.863281 61.765625 96 Z M 67.097656 61.652344 C 67.097656 61.117188 67.359375 60.609375 67.800781 60.304688 C 68.246094 60 68.808594 59.929688 69.3125 60.121094 C 70.378906 62.097656 70.207031 62.515625 69.894531 62.824219 C 69.585938 63.132812 69.164062 63.300781 68.726562 63.296875 C 68.292969 63.296875 67.875 63.125 67.570312 62.816406 C 67.265625 62.507812 67.097656 62.085938 67.105469 61.652344 Z M 83.683594 70.164062 C 82.617188 70.597656 81.558594 70.96875 80.539062 71.019531 C 79.007812 71.070312 77.503906 70.59375 76.28125 69.664062 C 74.820312 68.4375 73.777344 67.753906 73.339844 65.621094 C 73.1875 64.578125 73.214844 63.519531 73.425781 62.484375 C 73.796875 60.742188 73.382812 59.621094 72.148438 58.609375 C 71.152344 57.777344 69.878906 57.546875 68.480469 57.546875 C 68 57.519531 67.535156 57.375 67.125 57.128906 C 66.539062 56.84375 66.058594 56.117188 66.515625 55.222656 C 66.667969 54.933594 67.371094 54.230469 67.542969 54.101562 C 69.441406 53.023438 71.632812 53.375 73.652344 54.1875 C 75.53125 54.953125 76.949219 56.363281 78.992188 58.355469 C 81.078125 60.761719 81.457031 61.429688 82.644531 63.230469 C 83.585938 64.644531 84.4375 66.097656 85.019531 67.753906 C 85.375 68.792969 84.917969 69.644531 83.683594 70.164062 Z M 83.683594 70.164062 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
15
public/img/generic.svg
Normal file
15
public/img/generic.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="616.86328"
|
||||
height="616.86328"
|
||||
viewBox="0 0 77.10791 77.10791"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<path
|
||||
id="circle1"
|
||||
d="M 38.553955,0 A 38.554,38.554 0 0 0 0,38.553955 38.554,38.554 0 0 0 38.553955,77.107911 38.554,38.554 0 0 0 77.107911,38.553955 38.554,38.554 0 0 0 38.553955,0 Z m -3.125976,13.545898 h 6.251953 v 19.593751 l 16.968751,-9.796875 3.125974,5.414306 -16.968506,9.796875 16.968506,9.796877 -3.125974,5.414305 -16.968751,-9.796874 V 63.56201 H 35.427979 V 43.968263 l -16.968506,9.796874 -3.125977,-5.414305 16.96875,-9.796877 -16.96875,-9.796875 3.125977,-5.414306 16.968506,9.796875 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 803 B |
@@ -651,6 +651,19 @@
|
||||
<input type="number" id="n_openai" name="n_openai" class="text_pole" min="1" value="1">
|
||||
</div>
|
||||
</div>
|
||||
<div data-source="openrouter" class="range-block">
|
||||
<div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
|
||||
<span data-i18n="Middle-out Transform">Middle-out Transform</span>
|
||||
<a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<select id="openrouter_middleout" class="text_pole">
|
||||
<option value="auto" data-i18n="Auto">Auto</option>
|
||||
<option value="on" data-i18n="Allow">Allow</option>
|
||||
<option value="off" data-i18n="Forbid">Forbid</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div data-source="openrouter">
|
||||
Max prompt cost: <span id="openrouter_max_prompt_cost">Unknown</span>
|
||||
</div>
|
||||
@@ -669,7 +682,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
|
||||
<div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
|
||||
<div class="range-block-title" data-i18n="Temperature">
|
||||
Temperature
|
||||
</div>
|
||||
@@ -682,7 +695,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
|
||||
<div class="range-block-title" data-i18n="Frequency Penalty">
|
||||
Frequency Penalty
|
||||
</div>
|
||||
@@ -695,7 +708,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai">
|
||||
<div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek">
|
||||
<div class="range-block-title" data-i18n="Presence Penalty">
|
||||
Presence Penalty
|
||||
</div>
|
||||
@@ -721,7 +734,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
|
||||
<div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek">
|
||||
<div class="range-block-title" data-i18n="Top P">
|
||||
Top P
|
||||
</div>
|
||||
@@ -820,8 +833,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block m-t-1">
|
||||
<div class="range-block-title openai_restorable" data-i18n="World Info Format Template">
|
||||
<span>World Info format template</span>
|
||||
<div class="range-block-title openai_restorable">
|
||||
<span data-i18n="World Info Format Template">World Info format template</span>
|
||||
<div id="wi_format_restore" data-i18n="[title]Restore default format" title="Restore default format" class="right_menu_button">
|
||||
<div class="fa-solid fa-clock-rotate-left"></div>
|
||||
</div>
|
||||
@@ -958,7 +971,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq">
|
||||
<div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt">
|
||||
<div class="range-block-title justifyLeft" data-i18n="Seed">
|
||||
Seed
|
||||
</div>
|
||||
@@ -1235,7 +1248,8 @@
|
||||
<input class="neo-range-slider" type="range" id="temp_textgenerationwebui" name="volume" min="0.0" max="5.0" step="0.01" x-setting-id="temp">
|
||||
<input class="neo-range-input" type="number" min="0.0" max="5.0" step="0.01" data-for="temp_textgenerationwebui" id="temp_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<!-- Note: "except" mode = show for all BUT types in data-tg-type -->
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Top K">Top K</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" title="Top K sets a maximum amount of top tokens that can be chosen from. E.g Top K is 20, this means only the 20 highest ranking tokens will be kept (regardless of their probabilities being diverse or limited). Set to 0 (or -1, depending on your backend) to disable." data-i18n="[title]Top_K_desc"></div>
|
||||
@@ -1251,7 +1265,7 @@
|
||||
<input class="neo-range-slider" type="range" id="top_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
|
||||
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_p_textgenerationwebui" id="top_p_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Typical P">Typical P</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" title="Typical P Sampling prioritizes tokens based on their deviation from the average entropy of the set. It maintains tokens whose cumulative probability is close to a predefined threshold (e.g., 0.5), emphasizing those with average information content. Set to 1.0 to disable." data-i18n="[title]Typical_P_desc"></div>
|
||||
@@ -1259,7 +1273,7 @@
|
||||
<input class="neo-range-slider" type="range" id="typical_p_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
|
||||
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="typical_p_textgenerationwebui" id="typical_p_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Min P">Min P</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Min_P_desc" title="Min P sets a base minimum probability. This is scaled according to the top token's probability. E.g If Top token is 80% probability, and Min P is 0.1, only tokens higher than 8% would be considered. Set to 0 to disable."></div>
|
||||
@@ -1267,7 +1281,7 @@
|
||||
<input class="neo-range-slider" type="range" id="min_p_textgenerationwebui" name="volume" min="0" max="1" step="0.001">
|
||||
<input class="neo-range-input" type="number" min="0" max="1" step="0.001" data-for="min_p_textgenerationwebui" id="min_p_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Top A">Top A</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" title="Top A sets a threshold for token selection based on the square of the highest token probability. E.g if the Top-A value is 0.2 and the top token's probability is 50%, tokens with probabilities below 5% (0.2 * 0.5^2) are excluded. Set to 0 to disable." data-i18n="[title]Top_A_desc"></div>
|
||||
@@ -1275,7 +1289,7 @@
|
||||
<input class="neo-range-slider" type="range" id="top_a_textgenerationwebui" name="volume" min="0" max="1" step="0.01">
|
||||
<input class="neo-range-input" type="number" min="0" max="1" step="0.01" data-for="top_a_textgenerationwebui" id="top_a_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="TFS">TFS</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Tail_Free_Sampling_desc" title="Tail-Free Sampling (TFS) searches for a tail of low-probability tokens in the distribution, by analyzing the rate of change in token probabilities using derivatives. It retains tokens up to a threshold (e.g., 0.3) based on the normalized second derivative. The closer to 0, the more discarded tokens. Set to 1.0 to disable."></div>
|
||||
@@ -1307,7 +1321,7 @@
|
||||
<input class="neo-range-slider" type="range" id="eta_cutoff_textgenerationwebui" name="volume" min="0" max="20" step="0.01">
|
||||
<input class="neo-range-input" type="number" min="0" max="20" step="0.01" data-for="eta_cutoff_textgenerationwebui" id="eta_cutoff_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" class="alignitemscenter flex-container flexFlowColumn flexBasis30p flexGrow flexShrink gap0">
|
||||
<small data-i18n="rep.pen">Repetition Penalty</small>
|
||||
<input class="neo-range-slider" type="range" id="rep_pen_textgenerationwebui" name="volume" min="1" max="3" step="0.01">
|
||||
<input class="neo-range-input" type="number" min="1" max="3" step="0.01" data-for="rep_pen_textgenerationwebui" id="rep_pen_counter_textgenerationwebui">
|
||||
@@ -1425,7 +1439,7 @@
|
||||
<input class="neo-range-slider" type="range" id="dry_allowed_length_textgenerationwebui" min="1" max="20" step="1" />
|
||||
<input class="neo-range-input" type="number" min="1" max="20" step="1" data-for="dry_allowed_length_textgenerationwebui" id="dry_allowed_length_counter_textgenerationwebui">
|
||||
</div>
|
||||
<div class="alignItemsCenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" data-tg-type="llamacpp, koboldcpp, tabby">
|
||||
<div class="alignItemsCenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0" data-tg-type="llamacpp, koboldcpp, tabby, aphrodite">
|
||||
<small data-i18n="Penalty Range">Penalty Range</small>
|
||||
<input class="neo-range-slider" type="range" id="dry_penalty_last_n_textgenerationwebui" min="0" max="8192" step="1" />
|
||||
<input class="neo-range-input" type="number" min="0" max="8192" step="1" data-for="dry_penalty_last_n_textgenerationwebui" id="dry_penalty_last_n_counter_textgenerationwebui">
|
||||
@@ -1524,15 +1538,15 @@
|
||||
<div class="">
|
||||
<label class="checkbox_label" for="early_stopping_textgenerationwebui">
|
||||
<input type="checkbox" id="early_stopping_textgenerationwebui" />
|
||||
<small data-i18n="Early Stopping">Early Stopping
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
|
||||
</small>
|
||||
<small data-i18n="Early Stopping">Early Stopping</small>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates." title="Controls the stopping condition for beam search. If checked, the generation stops as soon as there are '# of Beams' sequences. If not checked, a heuristic is applied and the generation is stopped when it's very unlikely to find better candidates."></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tg-type="ooba" id="contrastiveSearchBlock" name="contrastiveSearchBlock" class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<h4 class="textAlignCenter" data-i18n="Contrastive search">Contrastive Search
|
||||
<h4 class="textAlignCenter">
|
||||
<span data-i18n="Contrastive search">Contrastive Search</span>
|
||||
<div class=" fa-solid fa-circle-info opacity50p " data-i18n="Contrastive_search_txt" title="A sampler that encourages diversity while maintaining coherence, by exploiting the isotropicity of the representation space of most LLMs. For details, see the paper A Contrastive Framework for Neural Text Generation by Su et al. (2022)."></div>
|
||||
</h4>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn wide100p gap0">
|
||||
@@ -1566,11 +1580,10 @@
|
||||
</label>
|
||||
<label data-tg-type="vllm, aphrodite, infermaticai" class="checkbox_label" for="ignore_eos_token_textgenerationwebui">
|
||||
<input type="checkbox" id="ignore_eos_token_textgenerationwebui" />
|
||||
<small data-i18n="Ignore EOS Token">Ignore EOS Token
|
||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
|
||||
</small>
|
||||
<small data-i18n="Ignore EOS Token">Ignore EOS Token</small>
|
||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Ignore the EOS Token even if it generates." title="Ignore the EOS Token even if it generates."></div>
|
||||
</label>
|
||||
<label class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
|
||||
<label data-tg-type-mode="except" data-tg-type="generic" class="checkbox_label flexGrow flexShrink" for="skip_special_tokens_textgenerationwebui">
|
||||
<input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
|
||||
<small data-i18n="Skip Special Tokens">Skip Special Tokens</small>
|
||||
</label>
|
||||
@@ -1595,14 +1608,14 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tg-type="mancer, ooba, koboldcpp, vllm, aphrodite, llamacpp, ollama, infermaticai, huggingface" class="flex-container flexFlowColumn alignitemscenter flexBasis48p flexGrow flexShrink gap0">
|
||||
<div data-tg-type="mancer, ooba, koboldcpp, vllm, aphrodite, llamacpp, ollama, infermaticai, huggingface, generic" class="flex-container flexFlowColumn alignitemscenter flexBasis48p flexGrow flexShrink gap0">
|
||||
<label>
|
||||
<small data-i18n="Seed">Seed</small>
|
||||
<div class="fa-solid fa-circle-info opacity50p " data-i18n="[title]Seed_desc" title="A random seed to use for deterministic and reproducable outputs. Set to -1 to use a random seed."></div>
|
||||
</label>
|
||||
<input type="number" id="seed_textgenerationwebui" class="text_pole textAlignCenter" min="-1" value="-1" />
|
||||
</div>
|
||||
<div id="banned_tokens_block_ooba" class="wide100p">
|
||||
<div data-tg-type-mode="except" data-tg-type="generic" id="banned_tokens_block_ooba" class="wide100p">
|
||||
<hr class="width100p">
|
||||
<h4 class="range-block-title justifyCenter">
|
||||
<span data-i18n="Banned Tokens">Banned Tokens/Strings</span>
|
||||
@@ -1629,7 +1642,8 @@
|
||||
</div>
|
||||
<div id="cfg_block_ooba" data-tg-type="ooba, tabby" class="wide100p">
|
||||
<hr class="width100p">
|
||||
<h4 data-i18n="CFG" class="textAlignCenter">CFG
|
||||
<h4 class="textAlignCenter">
|
||||
<span data-i18n="CFG">CFG</span>
|
||||
<div class="margin5 fa-solid fa-circle-info opacity50p " data-i18n="[title]Classifier Free Guidance. More helpful tip coming soon" title="Classifier Free Guidance. More helpful tip coming soon."></div>
|
||||
</h4>
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexShrink gap0">
|
||||
@@ -1735,6 +1749,7 @@
|
||||
<div data-name="tfs_z" draggable="true"><span>Tail Free Sampling</span><small></small></div>
|
||||
<div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
|
||||
<div data-name="xtc" draggable="true"><span>Exclude Top Choices</span><small></small></div>
|
||||
<div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
|
||||
</div>
|
||||
<div id="llamacpp_samplers_default_order" class="menu_button menu_button_icon">
|
||||
<span data-i18n="Load default order">Load default order</span>
|
||||
@@ -1774,6 +1789,35 @@
|
||||
<span data-i18n="Load default order">Load default order</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sampler_priority_block_aphrodite" data-tg-type="aphrodite" class="range-block flexFlowColumn wide100p">
|
||||
<hr class="wide100p">
|
||||
<h4 class="range-block-title justifyCenter">
|
||||
<span data-i18n="Sampler Order">Sampler Order</span>
|
||||
<div class="margin5 fa-solid fa-circle-info opacity50p" title="Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here." data-i18n="[title]Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here."></div>
|
||||
</h4>
|
||||
<div class="toggle-description widthUnset" data-i18n="Aphrodite only. Determines the order of samplers.">
|
||||
Aphrodite only. Determines the order of samplers.
|
||||
</div>
|
||||
<div id="sampler_priority_container_aphrodite" class="prompt_order">
|
||||
<div data-name="dry" draggable="true"><span>DRY</span><small></small></div>
|
||||
<div data-name="penalties" draggable="true"><span>Penalties</span><small></small></div>
|
||||
<div data-name="no_repeat_ngram" draggable="true"><span>No Repeat Ngram</span><small></small></div>
|
||||
<div data-name="temperature" draggable="true"><span>Dynatemp & Temperature</span><small></small></div>
|
||||
<div data-name="top_nsigma" draggable="true"><span>Top Nsigma</span><small></small></div>
|
||||
<div data-name="top_p_top_k" draggable="true"><span>Top P & Top K</span><small></small></div>
|
||||
<div data-name="top_a" draggable="true"><span>Top A</span><small></small></div>
|
||||
<div data-name="min_p" draggable="true"><span>Min P</span><small></small></div>
|
||||
<div data-name="tfs" draggable="true"><span>Tail-Free Sampling</span><small></small></div>
|
||||
<div data-name="eta_cutoff" draggable="true"><span>Eta Cutoff</span><small></small></div>
|
||||
<div data-name="epsilon_cutoff" draggable="true"><span>Epsilon Cutoff</span><small></small></div>
|
||||
<div data-name="typical_p" draggable="true"><span>Typical P</span><small></small></div>
|
||||
<div data-name="quadratic" draggable="true"><span>Cubic and Quadratic Sampling</span><small></small></div>
|
||||
<div data-name="xtc" draggable="true"><span>XTC</span><small></small></div>
|
||||
</div>
|
||||
<div id="aphrodite_default_order" class="menu_button menu_button_icon">
|
||||
<span data-i18n="Load default order">Load default order</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end of textgen settings-->
|
||||
<div id="openai_settings">
|
||||
@@ -1921,8 +1965,8 @@
|
||||
<label for="use_makersuite_sysprompt" class="checkbox_label widthFreeExpand">
|
||||
<input id="use_makersuite_sysprompt" type="checkbox" />
|
||||
<span>
|
||||
<span data-i18n="Use system prompt">Use system prompt</span><br>
|
||||
<small data-i18n="(Gemini 1.5 Pro/Flash only)">(Gemini 1.5 Pro/Flash only)</small>
|
||||
<span data-i18n="Use system prompt">Use system prompt</span>
|
||||
<i class="opacity50p fa-solid fa-circle-info" title="Gemini 1.5/2.0 Pro/Flash"></i>
|
||||
</span>
|
||||
</label>
|
||||
<div class="toggle-description justifyLeft marginBot5">
|
||||
@@ -1931,6 +1975,20 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="makersuite">
|
||||
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
|
||||
<input id="openai_show_thoughts" type="checkbox" />
|
||||
<span>
|
||||
<span data-i18n="Show model thoughts">Show model thoughts</span>
|
||||
<i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking"></i>
|
||||
</span>
|
||||
</label>
|
||||
<div class="toggle-description justifyLeft marginBot5">
|
||||
<span data-i18n="Display the model's internal thoughts in the response.">
|
||||
Display the model's internal thoughts in the response.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="claude">
|
||||
<div class="wide100p">
|
||||
<div class="flex-container alignItemsCenter">
|
||||
@@ -1959,23 +2017,16 @@
|
||||
Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.
|
||||
</span>
|
||||
</div>
|
||||
<div id="claude_human_sysprompt_message_block" class="wide100p">
|
||||
<div class="range-block-title openai_restorable">
|
||||
<span data-i18n="User first message">User first message</span>
|
||||
<div id="claude_human_sysprompt_message_restore" title="Restore User first message" data-i18n="[title]Restore User first message" class="right_menu_button">
|
||||
<div class="fa-solid fa-clock-rotate-left"></div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact autoSetHeight" rows="2" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc. Adds nothing when empty, i.e. requires a new prompt with the role 'user'."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block m-t-1" data-source="openai,openrouter,scale,custom">
|
||||
<div id="logit_bias_openai" class="range-block-title openai_restorable" data-i18n="Logit Bias">
|
||||
Logit Bias
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words">
|
||||
Helps to ban or reinforce the usage of certain tokens. Confirm token parsing with <a target="_blank" href="https://platform.openai.com/tokenizer/">Tokenizer</a>.
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="Helps to ban or reenforce the usage of certain words">Helps to ban or reinforce the usage of certain tokens.</span>
|
||||
<span data-i18n="Confirm token parsing with">Confirm token parsing with</span>
|
||||
<a target="_blank" href="https://platform.openai.com/tokenizer/" data-i18n="Tokenizer">Tokenizer</a>.
|
||||
</div>
|
||||
<div class="openai_logit_bias_preset_form">
|
||||
<select id="openai_logit_bias_preset">
|
||||
@@ -2029,7 +2080,7 @@
|
||||
<option value="textgenerationwebui" data-i18n="Text Completion">Text Completion</option>
|
||||
<option value="openai" data-i18n="Chat Completion">Chat Completion</option>
|
||||
<option value="novel" data-i18n="NovelAI">NovelAI</option>
|
||||
<option value="koboldhorde" data-i18n="KoboldAI Horde">KoboldAI Horde</option>
|
||||
<option value="koboldhorde" data-i18n="AI Horde">AI Horde</option>
|
||||
<option value="kobold" data-i18n="KoboldAI">KoboldAI Classic</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -2038,8 +2089,8 @@
|
||||
<div id="kobold_horde_block">
|
||||
<ul>
|
||||
<li>
|
||||
<a target="_blank" href="https://aihorde.net/" data-i18n="KoboldAI Horde Website">
|
||||
KoboldAI Horde Website
|
||||
<a target="_blank" href="https://aihorde.net/" data-i18n="AI Horde Website">
|
||||
AI Horde Website
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -2165,10 +2216,10 @@
|
||||
<div>
|
||||
<h4 data-i18n="API Type">API Type</h4>
|
||||
<select id="textgen_type">
|
||||
<option value="ooba" data-i18n="Default (completions compatible)">Default [OpenAI /completions compatible: oobabooga, LM Studio, etc.]</option>
|
||||
<option value="aphrodite">Aphrodite</option>
|
||||
<option value="dreamgen">DreamGen</option>
|
||||
<option value="featherless">Featherless</option>
|
||||
<option value="generic" data-i18n="Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]">Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]</option>
|
||||
<option value="huggingface">HuggingFace (Inference Endpoint)</option>
|
||||
<option value="infermaticai">InfermaticAI</option>
|
||||
<option value="koboldcpp">KoboldCpp</option>
|
||||
@@ -2177,6 +2228,7 @@
|
||||
<option value="ollama">Ollama</option>
|
||||
<option value="openrouter">OpenRouter</option>
|
||||
<option value="tabby">TabbyAPI</option>
|
||||
<option value="ooba">Text Generation WebUI (oobabooga)</option>
|
||||
<option value="togetherai">TogetherAI</option>
|
||||
<option value="vllm">vLLM</option>
|
||||
</select>
|
||||
@@ -2276,8 +2328,8 @@
|
||||
<div data-tg-type="mancer" class="flex-container flexFlowColumn">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
</div>
|
||||
<h4 data-i18n="Mancer API key">
|
||||
Mancer API key
|
||||
<h4>
|
||||
<span data-i18n="Mancer API key">Mancer API key</span>
|
||||
<a href="https://mancer.tech/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
@@ -2299,6 +2351,24 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div data-tg-type="generic" class="flex-container flexFlowColumn">
|
||||
<h4 data-i18n="API key (optional)">API key (optional)</h4>
|
||||
<div class="flex-container">
|
||||
<input id="api_key_generic" name="api_key_generic" class="text_pole flex1 wide100p" 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_generic">
|
||||
</div>
|
||||
</div>
|
||||
<div data-for="api_key_generic" class="neutral_warning" data-i18n="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 class="flex1">
|
||||
<h4 data-i18n="Server url">Server URL</h4>
|
||||
<small data-i18n="Example: 127.0.0.1:5000">Example: http://127.0.0.1:5000</small>
|
||||
<input id="generic_api_url_text" name="generic_api_url" class="text_pole wide100p" value="" autocomplete="off" data-server-history="generic">
|
||||
</div>
|
||||
<datalist id="generic_model_fill"></datalist>
|
||||
<input id="generic_model_textgenerationwebui" list="generic_model_fill" class="text_pole wide100p" placeholder="Model ID (optional)" data-i18n="[placeholder]Model ID (optional)" type="text">
|
||||
</div>
|
||||
<div data-tg-type="ooba" class="flex-container flexFlowColumn">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
|
||||
@@ -2573,15 +2643,21 @@
|
||||
<input id="koboldcpp_api_url_text" class="text_pole wide100p" value="" autocomplete="off" data-server-history="koboldcpp">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn marginTopBot5">
|
||||
<label data-tg-type="ooba, generic" class="checkbox_label" for="bypass_status_check_textgenerationwebui">
|
||||
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
|
||||
<span data-i18n="Bypass status check">Bypass status check</span>
|
||||
</label>
|
||||
<label data-tg-type="koboldcpp, llamacpp" class="checkbox_label" for="context_size_derived">
|
||||
<input type="checkbox" id="context_size_derived" />
|
||||
<span data-i18n="Derive context size from backend">Derive context size from backend</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<div id="api_button_textgenerationwebui" class="api_button menu_button menu_button_icon" type="submit" data-i18n="Connect" data-server-connect="ooba_blocking,vllm,aphrodite,tabby,koboldcpp,ollama,llamacpp,huggingface">Connect</div>
|
||||
<div data-tg-type="openrouter" class="menu_button menu_button_icon openrouter_authorize" title="Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai" data-i18n="Authorize;[title]Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai">Authorize</div>
|
||||
<div class="api_loading menu_button menu_button_icon" data-i18n="Cancel">Cancel</div>
|
||||
</div>
|
||||
<label data-tg-type="ooba" class="checkbox_label margin-bot-10px" for="bypass_status_check_textgenerationwebui">
|
||||
<input type="checkbox" id="bypass_status_check_textgenerationwebui" />
|
||||
<span data-i18n="Bypass status check">Bypass status check</span>
|
||||
</label>
|
||||
</form>
|
||||
<div class="online_status">
|
||||
<div class="online_status_indicator"></div>
|
||||
@@ -2603,6 +2679,7 @@
|
||||
<option value="blockentropy">Block Entropy</option>
|
||||
<option value="claude">Claude</option>
|
||||
<option value="cohere">Cohere</option>
|
||||
<option value="deepseek">DeepSeek</option>
|
||||
<option value="groq">Groq</option>
|
||||
<option value="makersuite">Google AI Studio</option>
|
||||
<option value="mistralai">MistralAI</option>
|
||||
@@ -2624,8 +2701,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="Saved addresses and passwords.">
|
||||
Saved addresses and passwords.<br>
|
||||
Saved addresses and passwords.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="openai_logit_bias_preset_form">
|
||||
<select id="openai_proxy_preset">
|
||||
@@ -2638,8 +2716,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft">
|
||||
<span data-i18n="This will show up as your saved preset.">
|
||||
This will show up as your saved preset.<br>
|
||||
This will show up as your saved preset.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input id="openai_reverse_proxy_name" type="text" class="text_pole" placeholder="..." />
|
||||
@@ -2649,8 +2728,9 @@
|
||||
</div>
|
||||
<div class="toggle-description justifyLeft wide100p">
|
||||
<span data-i18n="Alternative server URL (leave empty to use the default value).">
|
||||
Alternative server URL (leave empty to use the default value).<br>
|
||||
Alternative server URL (leave empty to use the default value).
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input id="openai_reverse_proxy" type="text" class="text_pole" placeholder="https://api.openai.com/v1" />
|
||||
@@ -2663,8 +2743,9 @@
|
||||
</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>
|
||||
Will be used as a password for the proxy instead of API key.
|
||||
</span>
|
||||
<br>
|
||||
</div>
|
||||
<div class="flex-container width100p">
|
||||
<input id="openai_proxy_password" type="password" class="text_pole flex1" placeholder="" form="openai_form" autocomplete="off" />
|
||||
@@ -2973,32 +3054,31 @@
|
||||
<optgroup label="Primary">
|
||||
<option value="gemini-1.5-pro">Gemini 1.5 Pro</option>
|
||||
<option value="gemini-1.5-flash">Gemini 1.5 Flash</option>
|
||||
<option value="gemini-1.0-pro">Gemini 1.0 Pro</option>
|
||||
<option value="gemini-pro">Gemini Pro (1.0)</option>
|
||||
<option value="gemini-pro-vision">Gemini Pro Vision (1.0)</option>
|
||||
<option value="gemini-1.0-pro">Gemini 1.0 Pro (Deprecated)</option>
|
||||
<option value="gemini-pro">Gemini Pro (1.0) (Deprecated)</option>
|
||||
<option value="gemini-ultra">Gemini Ultra (1.0)</option>
|
||||
<option value="gemini-1.0-ultra-latest">Gemini 1.0 Ultra</option>
|
||||
<option value="text-bison-001">PaLM 2 (Legacy)</option>
|
||||
<option value="chat-bison-001">PaLM 2 Chat (Legacy)</option>
|
||||
</optgroup>
|
||||
<optgroup label="Subversions">
|
||||
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
|
||||
<option value="gemini-2.0-flash-thinking-exp-1219">Gemini 2.0 Flash Thinking Experimental</option>
|
||||
<option value="gemini-2.0-flash-exp">Gemini 2.0 Flash Experimental</option>
|
||||
<option value="gemini-exp-1114">Gemini Experimental 2024-11-14</option>
|
||||
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experiment 2024-08-01</option>
|
||||
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option>
|
||||
<option value="gemini-exp-1121">Gemini Experimental 2024-11-21</option>
|
||||
<option value="gemini-exp-1206">Gemini Experimental 2024-12-06</option>
|
||||
<option value="gemini-1.5-pro-exp-0801">Gemini 1.5 Pro Experimental 2024-08-01</option>
|
||||
<option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experimental 2024-08-27</option>
|
||||
<option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option>
|
||||
<option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option>
|
||||
<option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option>
|
||||
<option value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</option>
|
||||
<option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option>
|
||||
<option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option>
|
||||
<option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option>
|
||||
<option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experimental 2024-08-27</option>
|
||||
<option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experimental 2024-08-27</option>
|
||||
<option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experimental 2024-09-24</option>
|
||||
<option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option>
|
||||
<option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option>
|
||||
<option value="gemini-1.5-flash-002">Gemini 1.5 Flash [002]</option>
|
||||
<option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option>
|
||||
<option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001]</option>
|
||||
<option value="gemini-1.0-pro-vision-latest">Gemini 1.0 Pro Vision [latest]</option>
|
||||
<option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest] (Deprecated)</option>
|
||||
<option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001] (Deprecated)</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
@@ -3067,6 +3147,9 @@
|
||||
</div>
|
||||
<h4 data-i18n="Groq Model">Groq Model</h4>
|
||||
<select id="model_groq_select">
|
||||
<optgroup label="Llama 3.3">
|
||||
<option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option>
|
||||
</optgroup>
|
||||
<optgroup label="Llama 3.2">
|
||||
<option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option>
|
||||
<option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option>
|
||||
@@ -3110,6 +3193,23 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="deepseek_form" data-source="deepseek">
|
||||
<h4 data-i18n="DeepSeek API Key">DeepSeek API Key</h4>
|
||||
<div class="flex-container">
|
||||
<input id="api_key_deepseek" name="api_key_deepseek" class="text_pole flex1" 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_deepseek"></div>
|
||||
</div>
|
||||
<div data-for="api_key_deepseek" class="neutral_warning" data-i18n="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>
|
||||
<h4 data-i18n="DeepSeek Model">DeepSeek Model</h4>
|
||||
<select id="model_deepseek_select">
|
||||
<option value="deepseek-chat">deepseek-chat</option>
|
||||
<option value="deepseek-coder">deepseek-coder</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="perplexity_form" data-source="perplexity">
|
||||
<h4 data-i18n="Perplexity API Key">Perplexity API Key</h4>
|
||||
<div class="flex-container">
|
||||
@@ -3163,16 +3263,17 @@
|
||||
<h4 data-i18n="Cohere Model">Cohere Model</h4>
|
||||
<select id="model_cohere_select">
|
||||
<optgroup label="Stable">
|
||||
<option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
|
||||
<option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
|
||||
<option value="c4ai-aya-23-35b">c4ai-aya-23-35b</option>
|
||||
<option value="c4ai-aya-23-8b">c4ai-aya-23-8b</option>
|
||||
<option value="c4ai-aya-23">c4ai-aya-23</option>
|
||||
<option value="c4ai-aya-expanse-8b">c4ai-aya-expanse-8b</option>
|
||||
<option value="c4ai-aya-expanse-32b">c4ai-aya-expanse-32b</option>
|
||||
<option value="command-light">command-light</option>
|
||||
<option value="command">command</option>
|
||||
<option value="command-r">command-r</option>
|
||||
<option value="command-r-plus">command-r-plus</option>
|
||||
<option value="command-r-08-2024">command-r-08-2024</option>
|
||||
<option value="command-r-plus-08-2024">command-r-plus-08-2024</option>
|
||||
<option value="command-r7b-12-2024">command-r7b-12-2024</option>
|
||||
</optgroup>
|
||||
<optgroup label="Nightly">
|
||||
<option value="command-light-nightly">command-light-nightly</option>
|
||||
@@ -3228,8 +3329,9 @@
|
||||
<h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4>
|
||||
<select id="custom_prompt_post_processing" class="text_pole" title="Applies additional processing to the prompt before sending it to the API." data-i18n="[title]Applies additional processing to the prompt before sending it to the API.">
|
||||
<option data-i18n="prompt_post_processing_none" value="">None</option>
|
||||
<option value="merge">Merge consecutive roles</option>
|
||||
<option value="strict">Strict (user first, alternating roles)</option>
|
||||
<option data-i18n="prompt_post_processing_merge" value="merge">Merge consecutive roles</option>
|
||||
<option data-i18n="prompt_post_processing_semi" value="semi">Semi-strict (alternating roles)</option>
|
||||
<option data-i18n="prompt_post_processing_strict" value="strict">Strict (user first, alternating roles)</option>
|
||||
</select>
|
||||
</form>
|
||||
<div id="01ai_form" data-source="01ai">
|
||||
@@ -3281,7 +3383,7 @@
|
||||
Advanced Formatting
|
||||
</span>
|
||||
|
||||
<a href="https://docs.sillytavern.app/usage/prompts/" class="notes-link" target="_blank">
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
@@ -3679,6 +3781,7 @@
|
||||
<option value="17">Mistral Nemo</option>
|
||||
<option value="8">Yi</option>
|
||||
<option value="11">Claude 1/2</option>
|
||||
<option value="18">DeepSeek V3</option>
|
||||
<option value="6">API (WebUI / koboldcpp)</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -3743,7 +3846,7 @@
|
||||
</div>
|
||||
<h3 class="margin0">
|
||||
<span data-i18n="Worlds/Lorebooks">Worlds/Lorebooks</span>
|
||||
<a href="https://docs.sillytavern.app/usage/worldinfo/" class="notes-link" target="_blank">
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
@@ -3752,7 +3855,11 @@
|
||||
<div id="wiTopBlock" class="flex-container">
|
||||
<div id="WIMultiSelector" class="flex1 flex alignSelfStart range-block">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
|
||||
<span>
|
||||
<small data-i18n="Active World(s) for all chats">
|
||||
Active World(s) for all chats
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range">
|
||||
<select id="world_info" class="select2_multi_sameline" multiple>
|
||||
@@ -3937,7 +4044,13 @@
|
||||
<div name="userSettingsRowOne" class="flex-container flexFlowRow alignitemscenter spaceBetween">
|
||||
<div class="flex-container">
|
||||
<div class="flex-container flexnowrap alignItemsBaseline">
|
||||
<h3 class="margin0"><span data-i18n="User Settings">User Settings</span></h3>
|
||||
<h3 class="margin0">
|
||||
<span data-i18n="User Settings">User Settings</span>
|
||||
|
||||
<a href="https://docs.sillytavern.app/usage/user_settings/" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div id="UI-language-block" class="flex-container alignItemsBaseline">
|
||||
@@ -4071,7 +4184,10 @@
|
||||
|
||||
<div class="alignitemscenter flex-container flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<small>
|
||||
<span data-i18n="Chat Width">Chat Width <i class="fa-solid fa-desktop"></i></span>
|
||||
<span>
|
||||
<span data-i18n="Chat Width">Chat Width</span>
|
||||
<i class="fa-solid fa-desktop"></i>
|
||||
</span>
|
||||
<div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]Width of the main chat window in % of screen width" title="Width of the main chat window in % of screen width"></div>
|
||||
</small>
|
||||
<input class="neo-range-slider" type="range" id="chat_width_slider" name="chat_width_slider" min="25" max="100" step="1">
|
||||
@@ -4167,7 +4283,7 @@
|
||||
</label>
|
||||
<label for="show_swipe_num_all_messages" class="checkbox_label" title="Display swipe numbers for all messages, not just the last." data-i18n="[title]Display swipe numbers for all messages, not just the last.">
|
||||
<input id="show_swipe_num_all_messages" type="checkbox" />
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i>
|
||||
<small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small>
|
||||
</label>
|
||||
<label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters">
|
||||
<input id="hotswapEnabled" type="checkbox" />
|
||||
@@ -4243,7 +4359,7 @@
|
||||
<div id="reload_chat" class="menu_button whitespacenowrap" data-i18n="[title]Reload and redraw the currently open chat" title="Reload and redraw the currently open chat.">
|
||||
<small data-i18n="Reload Chat">Reload Chat</small>
|
||||
</div>
|
||||
<div id="debug_menu" class="menu_button whitespacenowrap" data-i18n="Debug Menu">
|
||||
<div id="debug_menu" class="menu_button whitespacenowrap">
|
||||
<small data-i18n="Debug Menu">Debug Menu</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4270,9 +4386,6 @@
|
||||
<audio id="audio_message_sound" src="sounds/message.mp3" hidden></audio>
|
||||
<span>
|
||||
<small data-i18n="Message Sound">Message Sound</small>
|
||||
<a href="https://docs.sillytavern.app/usage/user_settings/uicustomization/#message-sound" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</span>
|
||||
</label>
|
||||
<label for="play_sound_unfocused" class="checkbox_label" title="Only play a sound when ST's browser tab is unfocused." data-i18n="[title]Only play a sound when ST's browser tab is unfocused">
|
||||
@@ -4365,8 +4478,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="examples-behavior-block">
|
||||
<label data-i18n="Example Messages Behavior">
|
||||
<small>Example Messages Behavior:</small>
|
||||
<label>
|
||||
<small data-i18n="Example Messages Behavior">
|
||||
Example Messages Behavior:
|
||||
</small>
|
||||
</label>
|
||||
<select id="example_messages_behavior">
|
||||
<option value="normal" data-i18n="Gradual push-out">Gradual push-out</option>
|
||||
@@ -4530,7 +4645,9 @@
|
||||
</label>
|
||||
<div class="flex-container">
|
||||
<div class="flex1" title="Determines how entries are found for autocomplete." data-i18n="[title]Determines how entries are found for autocomplete.">
|
||||
<label for="stscript_matching" data-i18n="Autocomplete Matching"><small>Matching</small></label>
|
||||
<label for="stscript_matching">
|
||||
<small data-i18n="Autocomplete Matching">Matching</small>
|
||||
</label>
|
||||
<select id="stscript_matching">
|
||||
<option data-i18n="Starts with" value="strict">Starts with</option>
|
||||
<option data-i18n="Includes" value="includes">Includes</option>
|
||||
@@ -4538,7 +4655,9 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex1" title="Sets the style of the autocomplete." data-i18n="[title]Sets the style of the autocomplete.">
|
||||
<label for="stscript_autocomplete_style" data-i18n="Autocomplete Style"><small>Style</small></label>
|
||||
<label for="stscript_autocomplete_style">
|
||||
<small data-i18n="Autocomplete Style">Style</small>
|
||||
</label>
|
||||
<div class="flex-container flexFlowRow alignItemsBaseline">
|
||||
<select id="stscript_autocomplete_style">
|
||||
<option data-i18n="Follow Theme" value="theme">Follow Theme</option>
|
||||
@@ -4550,8 +4669,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div title="Determines which keys select an item from the AutoComplete suggestions">
|
||||
<label data-i18n="Keyboard">
|
||||
<small>Keyboard:</small>
|
||||
<label>
|
||||
<small data-i18n="Keyboard">Keyboard:</small>
|
||||
</label>
|
||||
<select id="stscript_autocomplete_select">
|
||||
<option value="3" data-i18n="Select with Tab or Enter">Select with Tab or Enter</option>
|
||||
@@ -4565,7 +4684,9 @@
|
||||
<input class="neo-range-input" type="number" min="0.5" max="2" step="0.01" data-for="stscript_autocomplete_font_scale" id="stscript_autocomplete_font_scale_counter">
|
||||
</div>
|
||||
<div title="Sets the width of the autocomplete." data-i18n="[title]Sets the width of the autocomplete.">
|
||||
<label for="stscript_autocomplete_width" data-i18n="Autocomplete Width"><small>Width</small></label>
|
||||
<label for="stscript_autocomplete_width">
|
||||
<small data-i18n="Autocomplete Width">Width</small>
|
||||
</label>
|
||||
<div class="doubleRangeContainer">
|
||||
<div class="doubleRangeInputContainer">
|
||||
<input type="range" id="stscript_autocomplete_width_left" min="0" max="2" step="1">
|
||||
@@ -4593,14 +4714,18 @@
|
||||
<label><small data-i18n="Parser Flags">Parser Flags</small></label>
|
||||
<label class="checkbox_label" title="Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well." data-i18n="[title]Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.">
|
||||
<input id="stscript_parser_flag_strict_escaping" type="checkbox" />
|
||||
<span data-i18n="STRICT_ESCAPING"><small>STRICT_ESCAPING</small></span>
|
||||
<span>
|
||||
<small data-i18n="STRICT_ESCAPING">STRICT_ESCAPING</small>
|
||||
</span>
|
||||
<a href="https://docs.sillytavern.app/usage/st-script/#strict-escaping" target="_blank" class="notes-link">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</label>
|
||||
<label class="checkbox_label" title="Prevents {{getvar::}} {{getglobalvar::}} macros from having literal macro-like values auto-evaluated.
e.g. "{{newline}}" remains as literal string "{{newline}}"

(This is done by internally replacing {{getvar::}} {{getglobalvar::}} macros with scoped variables.)" data-i18n="[title]stscript_parser_flag_replace_getvar_label">
|
||||
<input id="stscript_parser_flag_replace_getvar" type="checkbox" />
|
||||
<span data-i18n="REPLACE_GETVAR"><small>REPLACE_GETVAR</small></span>
|
||||
<span>
|
||||
<small data-i18n="REPLACE_GETVAR">REPLACE_GETVAR</small>
|
||||
</span>
|
||||
<a href="https://docs.sillytavern.app/usage/st-script/#replace-variable-macros" target="_blank" class="notes-link">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
@@ -4623,6 +4748,13 @@
|
||||
Background Image
|
||||
</h3>
|
||||
<input id="bg-filter" data-i18n="[placeholder]Filter" placeholder="Filter" class="text_pole flex1" type="search" />
|
||||
<select id="background_fitting" class="text_pole" data-i18n="[title]Background Fitting" title="Background Fitting">
|
||||
<option value="classic" data-i18n="Classic">Classic</option>
|
||||
<option value="cover" data-i18n="Cover">Cover</option>
|
||||
<option value="contain" data-i18n="Contain">Contain</option>
|
||||
<option value="stretch" data-i18n="Stretch">Stretch</option>
|
||||
<option value="center" data-i18n="Center">Center</option>
|
||||
</select>
|
||||
<div id="auto_background" class="menu_button menu_button_icon" data-i18n="[title]Automatically select a background based on the chat context" title="Automatically select a background based on the chat context.">
|
||||
<i class="fa-solid fa-wand-magic"></i>
|
||||
<span data-i18n="Auto-select">Auto-select</span>
|
||||
@@ -4731,10 +4863,12 @@
|
||||
<div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
|
||||
<div class="flex-container alignItemsBaseline wide100p">
|
||||
<div class="flex1 flex-container alignItemsBaseline">
|
||||
<h3 class="margin0" data-i18n="Persona Management">Persona Management</h3>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank" data-i18n="How do I use this?">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
<h3 class="margin0" >
|
||||
<span data-i18n="Persona Management">Persona Management</span>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<div class="menu_button menu_button_icon user_stats_button" data-i18n="[title]Click for stats!" title="Click for stats!">
|
||||
@@ -4786,6 +4920,8 @@
|
||||
</div>
|
||||
<div id="sync_name_button" class="menu_button fa-solid fa-sync" title="Click to set user name for all messages" data-i18n="[title]Click to set user name for all messages">
|
||||
</div>
|
||||
<div id="persona_lore_button" class="menu_button fa-solid fa-globe" title="Persona Lore Alt+Click to open the lorebook" data-i18n="[title]Persona Lore Alt+Click to open the lorebook">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 data-i18n="Persona Description">Persona Description</h4>
|
||||
@@ -4902,7 +5038,7 @@
|
||||
<input type="hidden" id="fav_checkbox" name="fav" />
|
||||
<div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
|
||||
<div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore Click to load Shift-click to open 'Link to World Info' popup" data-i18n="[title]world_button_title"></div>
|
||||
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
|
||||
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>
|
||||
<div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
|
||||
<!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
|
||||
<!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
|
||||
@@ -5455,7 +5591,12 @@
|
||||
<span data-i18n="Examples of dialogue" class="mdhotkey_location">Examples of dialogue</span>
|
||||
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="mes_example_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
||||
</h4>
|
||||
<h5 data-i18n="Important to set the character's writing style.">Important to set the character's writing style. <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a></h5>
|
||||
<h5>
|
||||
<span data-i18n="Important to set the character's writing style.">Important to set the character's writing style.</span>
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank">
|
||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<textarea id="mes_example_textarea" class="flexGrow mdHotkeys" name="mes_example" data-i18n="[placeholder](Examples of chat dialog. Begin each example with START on a new line.)" placeholder="(Examples of chat dialog. Begin each example with <START> on a new line.)" form="form_create" rows="6"></textarea>
|
||||
<div class="extension_token_counter">
|
||||
@@ -5507,31 +5648,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- templates for JS to reuse when needed -->
|
||||
<div id="chat_world_template" class="template_element">
|
||||
<div class="chat_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
|
||||
<span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
|
||||
<span data-i18n="chat_world_template_txt">
|
||||
A selected World Info will be bound to this chat. When generating an AI reply,
|
||||
it will be combined with the entries from global and character lorebooks.
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range wide100p">
|
||||
<select class="chat_world_info_selector wide100p">
|
||||
<option value="">--- None ---</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="character_world_template" class="template_element">
|
||||
<div class="character_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h3>
|
||||
<span data-i18n="Select a World Info file for"> Select a World Info file for <span class="character_name"></span></span>:
|
||||
<span data-i18n="Select a World Info file for">Select a World Info file for</span>
|
||||
<span class="character_name"></span>:
|
||||
</h3>
|
||||
</div>
|
||||
<h4 data-i18n="Primary Lorebook">Primary Lorebook</h4>
|
||||
@@ -5832,7 +5954,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="sticky" data-i18n="Sticky">
|
||||
<small class="flex-container alignItemsBaseline" for="sticky">
|
||||
<span data-i18n="Sticky">
|
||||
Sticky
|
||||
</span>
|
||||
@@ -5845,7 +5967,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="cooldown" data-i18n="Cooldown">
|
||||
<small class="flex-container alignItemsBaseline" for="cooldown">
|
||||
<span data-i18n="Cooldown">
|
||||
Cooldown
|
||||
</span>
|
||||
@@ -5858,7 +5980,7 @@
|
||||
</div>
|
||||
<div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
|
||||
<div class="flex-container justifySpaceBetween marginBot5">
|
||||
<small class="flex-container alignItemsBaseline" for="delay" data-i18n="Delay">
|
||||
<small class="flex-container alignItemsBaseline" for="delay">
|
||||
<span data-i18n="Delay">
|
||||
Delay
|
||||
</span>
|
||||
@@ -5943,6 +6065,7 @@
|
||||
</div>
|
||||
<div id="openai_logit_bias_template" class="template_element">
|
||||
<div class="openai_logit_bias_form">
|
||||
<span class="drag-handle">☰</span>
|
||||
<input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Text or token ids" placeholder="Text or [token ids]" />
|
||||
<input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
|
||||
<i class="menu_button fa-solid fa-xmark openai_logit_bias_remove"></i>
|
||||
@@ -5950,6 +6073,7 @@
|
||||
</div>
|
||||
<div id="logit_bias_template" class="template_element">
|
||||
<div class="logit_bias_form">
|
||||
<span class="drag-handle">☰</span>
|
||||
<input class="logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
|
||||
<input class="logit_bias_value text_pole" type="number" min="-100" value="0" max="100" step="0.01" />
|
||||
<i class="menu_button fa-solid fa-xmark logit_bias_remove"></i>
|
||||
@@ -5989,7 +6113,9 @@
|
||||
<label for="completion_prompt_manager_popup_entry_form_role">
|
||||
<span data-i18n="Role">Role</span>
|
||||
</label>
|
||||
<div class="text_muted" data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</div>
|
||||
<div class="text_muted">
|
||||
<span data-i18n="To whom this message will be attributed.">To whom this message will be attributed.</span>
|
||||
</div>
|
||||
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
|
||||
<option data-i18n="System" value="system">System</option>
|
||||
<option data-i18n="User" value="user">User</option>
|
||||
|
@@ -5,7 +5,8 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowUmdGlobalAccess": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictBindCallApply": true
|
||||
},
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
|
@@ -19,6 +19,7 @@ import seedrandom from 'seedrandom';
|
||||
import * as Popper from '@popperjs/core';
|
||||
import droll from 'droll';
|
||||
import morphdom from 'morphdom';
|
||||
import { toggle as slideToggle } from 'slidetoggle';
|
||||
|
||||
/**
|
||||
* Expose the libraries to the 'window' object.
|
||||
@@ -94,6 +95,7 @@ export default {
|
||||
Popper,
|
||||
droll,
|
||||
morphdom,
|
||||
slideToggle,
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -115,4 +117,5 @@ export {
|
||||
Popper,
|
||||
droll,
|
||||
morphdom,
|
||||
slideToggle,
|
||||
};
|
||||
|
@@ -95,13 +95,14 @@ EventEmitter.prototype.removeListener = function (event, listener) {
|
||||
};
|
||||
|
||||
EventEmitter.prototype.emit = async function (event) {
|
||||
let args = [].slice.call(arguments, 1);
|
||||
if (localStorage.getItem('eventTracing') === 'true') {
|
||||
console.trace('Event emitted: ' + event, args);
|
||||
} else {
|
||||
console.debug('Event emitted: ' + event);
|
||||
}
|
||||
|
||||
var i, listeners, length, args = [].slice.call(arguments, 1);
|
||||
let i, listeners, length;
|
||||
|
||||
if (typeof this.events[event] === 'object') {
|
||||
listeners = this.events[event].slice();
|
||||
@@ -120,13 +121,14 @@ EventEmitter.prototype.emit = async function (event) {
|
||||
};
|
||||
|
||||
EventEmitter.prototype.emitAndWait = function (event) {
|
||||
let args = [].slice.call(arguments, 1);
|
||||
if (localStorage.getItem('eventTracing') === 'true') {
|
||||
console.trace('Event emitted: ' + event, args);
|
||||
} else {
|
||||
console.debug('Event emitted: ' + event);
|
||||
}
|
||||
|
||||
var i, listeners, length, args = [].slice.call(arguments, 1);
|
||||
let i, listeners, length;
|
||||
|
||||
if (typeof this.events[event] === 'object') {
|
||||
listeners = this.events[event].slice();
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "اكتمال النص",
|
||||
"Chat Completion": "إكمال الدردشة",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "جماعة KoboldAI",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "تجنب إرسال معلومات حساسة إلى الجماعة.",
|
||||
"Review the Privacy statement": "مراجعة بيان الخصوصية",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "قم بتشغيل تطبيق DrawThings مع تمكين مفتاح HTTP API في واجهة المستخدم! يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
|
||||
"sd_vlad_url": "مثال: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "يجب أن يكون الخادم قابلاً للوصول من الجهاز المضيف SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "تلميح: احفظ مفتاح API في إعدادات Horde KoboldAI API لاستخدامه هنا.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "تلميح: احفظ مفتاح API في إعدادات AI Horde API لاستخدامه هنا.",
|
||||
"Allow NSFW images from Horde": "السماح بصور NSFW من Horde",
|
||||
"Sanitize prompts (recommended)": "مطالبات التعقيم (مستحسن)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "قم بضبط معلمات الإنشاء تلقائيًا لضمان إنشاء صور مجانية.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Textvervollständigung",
|
||||
"Chat Completion": "Chat-Abschluss",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Vermeide das Senden sensibler Informationen an die Horde.",
|
||||
"Review the Privacy statement": "Überprüfe die Datenschutzerklärung",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "Führen Sie die DrawThings-App mit aktiviertem HTTP-API-Schalter in der Benutzeroberfläche aus! Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
|
||||
"sd_vlad_url": "Beispiel: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Der Server muss vom SillyTavern-Hostcomputer aus zugänglich sein.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Tipp: Speichern Sie einen API-Schlüssel in den Horde KoboldAI API-Einstellungen, um ihn hier zu verwenden.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Tipp: Speichern Sie einen API-Schlüssel in den AI Horde API-Einstellungen, um ihn hier zu verwenden.",
|
||||
"Allow NSFW images from Horde": "NSFW-Bilder von Horde zulassen",
|
||||
"Sanitize prompts (recommended)": "Eingabeaufforderungen bereinigen (empfohlen)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Passen Sie die Generierungsparameter automatisch an, um eine freie Bildgenerierung zu gewährleisten.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Completar texto",
|
||||
"Chat Completion": "Finalización del chat",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "Horde de KoboldAI",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Evite enviar información sensible a Horde.",
|
||||
"Review the Privacy statement": "Revise la declaración de privacidad",
|
||||
@@ -874,7 +874,7 @@
|
||||
"Bulk_edit_characters": "Editar personajes masivamente",
|
||||
"Bulk select all characters": "Seleccionar de forma masiva todos los personajes",
|
||||
"Bulk delete characters": "Eliminar personajes masivamente",
|
||||
"popup-button-save": "Ahorrar",
|
||||
"popup-button-save": "Guardar",
|
||||
"popup-button-yes": "Sí",
|
||||
"popup-button-no": "No",
|
||||
"popup-button-cancel": "Cancelar",
|
||||
@@ -1019,7 +1019,7 @@
|
||||
"This prompt cannot be overridden by character cards, even if overrides are preferred.": "Este mensaje no puede ser anulado por tarjetas de personaje, incluso si se prefieren las anulaciones.",
|
||||
"prompt_manager_forbid_overrides": "Prohibir anulaciones",
|
||||
"reset": "reiniciar",
|
||||
"save": "ahorrar",
|
||||
"save": "guardar",
|
||||
"This message is invisible for the AI": "Este mensaje es invisible para la IA",
|
||||
"Message Actions": "Acciones de mensajes",
|
||||
"Translate message": "Traducir mensaje",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "¡Ejecute la aplicación DrawThings con el interruptor API HTTP habilitado en la interfaz de usuario! Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
|
||||
"sd_vlad_url": "Ejemplo: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Se debe poder acceder al servidor desde la máquina host de SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Sugerencia: guarde una clave API en la configuración de API de Horde KoboldAI para usarla aquí.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Sugerencia: guarde una clave API en la configuración de API de AI Horde para usarla aquí.",
|
||||
"Allow NSFW images from Horde": "Permitir imágenes NSFW de Horda",
|
||||
"Sanitize prompts (recommended)": "Indicaciones para desinfectar (recomendado)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Ajuste automáticamente los parámetros de generación para garantizar generaciones de imágenes gratuitas.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Achèvement du texte",
|
||||
"Chat Completion": "Achèvement du chat",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "Horde KoboldAI",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Évitez d'envoyer des informations sensibles à la Horde.",
|
||||
"Review the Privacy statement": "Examiner la déclaration de confidentialité",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "exécutez l'application DrawThings avec le commutateur API HTTP activé dans l'interface utilisateur ! Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
|
||||
"sd_vlad_url": "Exemple : {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Le serveur doit être accessible depuis la machine hôte de SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API Horde KoboldAI pour l'utiliser ici.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Astuce : enregistrez une clé API dans les paramètres de l'API AI Horde pour l'utiliser ici.",
|
||||
"Allow NSFW images from Horde": "Autoriser les images NSFW de la Horde",
|
||||
"Sanitize prompts (recommended)": "Désinfecter les invites (recommandé)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Ajustez automatiquement les paramètres de génération pour garantir des générations d’images gratuites.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Textaútfylling",
|
||||
"Chat Completion": "Spjalllokun",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Hópur",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Forðastu að senda viðkvæm gögn til Hórdans.",
|
||||
"Review the Privacy statement": "Farið yfir Persónuverndarskýrsluna",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "keyrðu DrawThings app með HTTP API rofi virkt í notendaviðmótinu! Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
|
||||
"sd_vlad_url": "Dæmi: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Miðlarinn verður að vera aðgengilegur frá SillyTavern hýsingarvélinni.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Ábending: Vistaðu API lykil í Horde KoboldAI API stillingum til að nota hann hér.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Ábending: Vistaðu API lykil í AI Horde API stillingum til að nota hann hér.",
|
||||
"Allow NSFW images from Horde": "Leyfa NSFW myndir frá Horde",
|
||||
"Sanitize prompts (recommended)": "Hreinsunarleiðbeiningar (ráðlagt)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Stilltu kynslóðarbreytur sjálfkrafa til að tryggja ókeypis myndmyndun.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Completamento del testo",
|
||||
"Chat Completion": "Completamento della chat",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "Orda di KoboldAI",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Evita di inviare informazioni sensibili all'Orda.",
|
||||
"Review the Privacy statement": "Revisione della dichiarazione sulla privacy",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "esegui l'app DrawThings con lo switch API HTTP abilitato nell'interfaccia utente! Il server deve essere accessibile dalla macchina host SillyTavern.",
|
||||
"sd_vlad_url": "Esempio: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Il server deve essere accessibile dalla macchina host SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Suggerimento: salva una chiave API nelle impostazioni API Horde KoboldAI per usarla qui.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Suggerimento: salva una chiave API nelle impostazioni API AI Horde per usarla qui.",
|
||||
"Allow NSFW images from Horde": "Consenti immagini NSFW da Horde",
|
||||
"Sanitize prompts (recommended)": "Messaggi di disinfezione (consigliato)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Regola automaticamente i parametri di generazione per garantire generazioni di immagini gratuite.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "テキスト補完",
|
||||
"Chat Completion": "チャット完了",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Hordeに機密情報を送信しないでください。",
|
||||
"Review the Privacy statement": "プライバシー声明を確認する",
|
||||
@@ -1274,6 +1274,8 @@
|
||||
"sd_Raw_Last_Message": "生の最後のメッセージ",
|
||||
"sd_Background": "背景",
|
||||
"Image Generation": "画像生成",
|
||||
"Stop Image Generation": "画像生成を停止",
|
||||
"Generate Caption": "画像説明を生成",
|
||||
"sd_refine_mode": "プロンプトを生成 API に送信する前に手動で編集できるようにする",
|
||||
"sd_refine_mode_txt": "生成前にプロンプトを編集する",
|
||||
"sd_interactive_mode": "「猫の写真を送ってください」のようなメッセージを送信するときに、画像を自動的に生成します。",
|
||||
@@ -1295,7 +1297,7 @@
|
||||
"sd_drawthings_auth_txt": "UI で HTTP API スイッチを有効にして DrawThings アプリを実行します。サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
|
||||
"sd_vlad_url": "例: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "サーバーは SillyTavern ホスト マシンからアクセスできる必要があります。",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "ヒント: ここで使用するには、Horde KoboldAI API 設定に API キーを保存してください。",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "ヒント: ここで使用するには、AI Horde API 設定に API キーを保存してください。",
|
||||
"Allow NSFW images from Horde": "HordeからのNSFW画像を許可する",
|
||||
"Sanitize prompts (recommended)": "サニタイズプロンプト(推奨)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "生成パラメータを自動的に調整して、自由な画像生成を保証します。",
|
||||
@@ -1439,5 +1441,8 @@
|
||||
"Still have questions?": "まだ質問がありますか?",
|
||||
"Join the SillyTavern Discord": "SillyTavernのDiscordに参加",
|
||||
"Post a GitHub issue": "GitHubの問題を投稿",
|
||||
"Contact the developers": "開発者に連絡"
|
||||
"Contact the developers": "開発者に連絡",
|
||||
"Stop Inspecting": "検査を停止",
|
||||
"Inspect Prompts": "プロンプトを検査",
|
||||
"Toggle prompt inspection": "プロンプト検査の切り替え"
|
||||
}
|
||||
|
@@ -269,7 +269,7 @@
|
||||
"Text Completion": "Text Completion",
|
||||
"Chat Completion": "Chat Completion",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "민감한 정보를 Horde에 보내지 않도록 합니다.",
|
||||
"Review the Privacy statement": "개인 정보 보호 정책 검토",
|
||||
@@ -1312,7 +1312,7 @@
|
||||
"sd_drawthings_auth_txt": "UI에서 HTTP API 스위치가 활성화된 상태에서 DrawThings 앱을 실행하세요! SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
|
||||
"sd_vlad_url": "예: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "SillyTavern 호스트 시스템에서 서버에 액세스할 수 있어야 합니다.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "힌트: 여기에서 사용하려면 Horde KoboldAI API 설정에 API 키를 저장하세요.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "힌트: 여기에서 사용하려면 AI Horde API 설정에 API 키를 저장하세요.",
|
||||
"Allow NSFW images from Horde": "Horde의 NSFW 이미지 허용",
|
||||
"Sanitize prompts (recommended)": "프롬프트 삭제(권장)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "무료 이미지 생성을 보장하기 위해 생성 매개변수를 자동으로 조정합니다.",
|
||||
@@ -1623,4 +1623,4 @@
|
||||
"Master Import": "마스터 불러오기",
|
||||
"Master Export": "마스터 내보내기",
|
||||
"Chat Quick Reply Sets": "채팅 빠른 답장 세트들"
|
||||
}
|
||||
}
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Tekstvoltooiing",
|
||||
"Chat Completion": "Chat-voltooiing",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Vermijd het verzenden van gevoelige informatie naar de Horde.",
|
||||
"Review the Privacy statement": "Bekijk de privacyverklaring",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "voer de DrawThings-app uit met HTTP API-switch ingeschakeld in de gebruikersinterface! De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
|
||||
"sd_vlad_url": "Voorbeeld: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "De server moet toegankelijk zijn vanaf de SillyTavern-hostmachine.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Tip: sla een API-sleutel op in de Horde KoboldAI API-instellingen om deze hier te gebruiken.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Tip: sla een API-sleutel op in de AI Horde API-instellingen om deze hier te gebruiken.",
|
||||
"Allow NSFW images from Horde": "Sta NSFW-afbeeldingen van Horde toe",
|
||||
"Sanitize prompts (recommended)": "Ontsmettingsmeldingen (aanbevolen)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Pas de generatieparameters automatisch aan om vrije beeldgeneraties te garanderen.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Conclusão de texto",
|
||||
"Chat Completion": "Conclusão do bate-papo",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "Horda KoboldAI",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Evite enviar informações sensíveis para a Horda.",
|
||||
"Review the Privacy statement": "Reveja a declaração de privacidade",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "execute o aplicativo DrawThings com a opção HTTP API habilitada na IU! O servidor deve estar acessível a partir da máquina host SillyTavern.",
|
||||
"sd_vlad_url": "Exemplo: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "O servidor deve estar acessível a partir da máquina host SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Dica: salve uma chave de API nas configurações da API Horde KoboldAI para usá-la aqui.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Dica: salve uma chave de API nas configurações da API AI Horde para usá-la aqui.",
|
||||
"Allow NSFW images from Horde": "Permitir imagens NSFW da Horda",
|
||||
"Sanitize prompts (recommended)": "Solicitações de higienização (recomendado)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Ajuste automaticamente os parâmetros de geração para garantir gerações de imagens livres.",
|
||||
|
@@ -299,7 +299,7 @@
|
||||
"Example: http://127.0.0.1:5000/api ": "Пример: http://127.0.0.1:5000/api",
|
||||
"No connection...": "Нет соединения...",
|
||||
"Get your NovelAI API Key": "Получите свой API-ключ для NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"NovelAI": "NovelAI",
|
||||
"OpenAI API key": "Ключ для API OpenAI",
|
||||
"Trim spaces": "Обрезать пробелы",
|
||||
@@ -1461,7 +1461,7 @@
|
||||
"sd_drawthings_auth_txt": "run DrawThings app with HTTP API switch enabled in the UI! The server must be accessible from the SillyTavern host machine.",
|
||||
"sd_vlad_url": "Example: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "The server must be accessible from the SillyTavern host machine.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Hint: Save an API key in Horde KoboldAI API settings to use it here.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Hint: Save an API key in AI Horde API settings to use it here.",
|
||||
"Allow NSFW images from Horde": "Разрешить NSFW-картинки в Horde",
|
||||
"Sanitize prompts (recommended)": "Sanitize prompts (recommended)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Automatically adjust generation parameters to ensure free image generations.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Завершення тексту",
|
||||
"Chat Completion": "Завершення чату",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Уникайте надсилання чутливої інформації в Horde.",
|
||||
"Review the Privacy statement": "Перегляньте заяву про конфіденційність",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "запустіть програму DrawThings із увімкненим перемикачем HTTP API в інтерфейсі користувача! Сервер має бути доступним із хост-машини SillyTavern.",
|
||||
"sd_vlad_url": "Приклад: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Сервер має бути доступним із хост-машини SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Підказка: збережіть ключ API в налаштуваннях Horde KoboldAI API, щоб використовувати його тут.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Підказка: збережіть ключ API в налаштуваннях AI Horde API, щоб використовувати його тут.",
|
||||
"Allow NSFW images from Horde": "Дозволити зображення NSFW від Horde",
|
||||
"Sanitize prompts (recommended)": "Очистити підказки (рекомендовано)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Автоматично налаштовуйте параметри генерації, щоб забезпечити вільне створення зображень.",
|
||||
|
@@ -267,7 +267,7 @@
|
||||
"Text Completion": "Text Completion",
|
||||
"Chat Completion": "Chat Completion",
|
||||
"NovelAI": "NovelAI",
|
||||
"KoboldAI Horde": "KoboldAI Horde",
|
||||
"AI Horde": "AI Horde",
|
||||
"KoboldAI": "KoboldAI",
|
||||
"Avoid sending sensitive information to the Horde.": "Tránh gửi thông tin nhạy cảm cho Horde.",
|
||||
"Review the Privacy statement": "Xem lại Chính sách bảo mật",
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"sd_drawthings_auth_txt": "chạy ứng dụng DrawThings với tính năng chuyển đổi API HTTP được bật trong giao diện người dùng! Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
|
||||
"sd_vlad_url": "Ví dụ: {{vlad_url}}",
|
||||
"The server must be accessible from the SillyTavern host machine.": "Máy chủ phải có thể truy cập được từ máy chủ SillyTavern.",
|
||||
"Hint: Save an API key in Horde KoboldAI API settings to use it here.": "Gợi ý: Lưu khóa API trong cài đặt API Horde KoboldAI để sử dụng tại đây.",
|
||||
"Hint: Save an API key in AI Horde API settings to use it here.": "Gợi ý: Lưu khóa API trong cài đặt API AI Horde để sử dụng tại đây.",
|
||||
"Allow NSFW images from Horde": "Cho phép hình ảnh NSFW từ Horde",
|
||||
"Sanitize prompts (recommended)": "Nhắc nhở vệ sinh (khuyến nghị)",
|
||||
"Automatically adjust generation parameters to ensure free image generations.": "Tự động điều chỉnh các thông số tạo để đảm bảo tạo ra hình ảnh miễn phí.",
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
681
public/script.js
681
public/script.js
File diff suppressed because it is too large
Load Diff
@@ -1208,7 +1208,7 @@ class PromptManager {
|
||||
const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block');
|
||||
|
||||
nameField.value = prompt.name ?? '';
|
||||
roleField.value = prompt.role ?? 'system';
|
||||
roleField.value = prompt.role || 'system';
|
||||
promptField.value = prompt.content ?? '';
|
||||
promptField.disabled = prompt.marker ?? false;
|
||||
injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE;
|
||||
@@ -1518,7 +1518,7 @@ class PromptManager {
|
||||
let detachSpanHtml = '';
|
||||
if (this.isPromptDeletionAllowed(prompt)) {
|
||||
detachSpanHtml = `
|
||||
<span title="Remove" class="prompt-manager-detach-action caution fa-solid fa-chain-broken"></span>
|
||||
<span title="Remove" class="prompt-manager-detach-action caution fa-solid fa-chain-broken fa-xs"></span>
|
||||
`;
|
||||
} else {
|
||||
detachSpanHtml = '<span class="fa-solid"></span>';
|
||||
@@ -1527,7 +1527,7 @@ class PromptManager {
|
||||
let editSpanHtml = '';
|
||||
if (this.isPromptEditAllowed(prompt)) {
|
||||
editSpanHtml = `
|
||||
<span title="edit" class="prompt-manager-edit-action fa-solid fa-pencil"></span>
|
||||
<span title="edit" class="prompt-manager-edit-action fa-solid fa-pencil fa-xs"></span>
|
||||
`;
|
||||
} else {
|
||||
editSpanHtml = '<span class="fa-solid"></span>';
|
||||
@@ -1550,16 +1550,27 @@ class PromptManager {
|
||||
const isInjectionPrompt = prompt.injection_position === INJECTION_POSITION.ABSOLUTE;
|
||||
const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier);
|
||||
const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : '';
|
||||
const iconLookup = prompt.role === 'system' && (prompt.marker || prompt.system_prompt) ? '' : prompt.role;
|
||||
|
||||
//add role icons to the right of prompt name
|
||||
const promptRoles = {
|
||||
assistant: { roleIcon: 'fa-robot', roleTitle: 'Prompt will be sent as Assistant' },
|
||||
user: { roleIcon: 'fa-user', roleTitle: 'Prompt will be sent as User' },
|
||||
};
|
||||
const roleIcon = promptRoles[iconLookup]?.roleIcon || '';
|
||||
const roleTitle = promptRoles[iconLookup]?.roleTitle || '';
|
||||
|
||||
listItemHtml += `
|
||||
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${prompt.identifier}">
|
||||
<li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${escapeHtml(prompt.identifier)}">
|
||||
<span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}">
|
||||
${isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''}
|
||||
${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''}
|
||||
${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''}
|
||||
${isUserPrompt ? '<span class="fa-fw fa-solid fa-user" title="User Prompt"></span>' : ''}
|
||||
${isUserPrompt ? '<span class="fa-fw fa-solid fa-asterisk" title="Preset Prompt"></span>' : ''}
|
||||
${isInjectionPrompt ? '<span class="fa-fw fa-solid fa-syringe" title="In-Chat Injection"></span>' : ''}
|
||||
${this.isPromptInspectionAllowed(prompt) ? `<a title="${encodedName}" class="prompt-manager-inspect-action">${encodedName}</a>` : `<span title="${encodedName}">${encodedName}</span>`}
|
||||
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${prompt.injection_depth}</small>` : ''}
|
||||
${roleIcon ? `<span data-role="${escapeHtml(prompt.role)}" class="fa-xs fa-solid ${roleIcon}" title="${roleTitle}"></span>` : ''}
|
||||
${isInjectionPrompt ? `<small class="prompt-manager-injection-depth">@ ${escapeHtml(prompt.injection_depth)}</small>` : ''}
|
||||
${isOverriddenPrompt ? '<small class="fa-solid fa-address-card prompt-manager-overridden" title="Pulled from a character card"></small>' : ''}
|
||||
</span>
|
||||
<span>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { DOMPurify, Bowser } from '../lib.js';
|
||||
import { DOMPurify, Bowser, slideToggle } from '../lib.js';
|
||||
|
||||
import {
|
||||
characters,
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
menu_type,
|
||||
substituteParams,
|
||||
sendTextareaMessage,
|
||||
getSlideToggleOptions,
|
||||
} from '../script.js';
|
||||
|
||||
import {
|
||||
@@ -315,7 +316,7 @@ function RA_checkOnlineStatus() {
|
||||
if (online_status == 'no_connection') {
|
||||
const send_textarea = $('#send_textarea');
|
||||
send_textarea.attr('placeholder', send_textarea.attr('no_connection_text')); //Input bar placeholder tells users they are not connected
|
||||
//$('#send_form').addClass('no-connection'); //entire input form area is red when not connected
|
||||
$('#send_form').addClass('no-connection');
|
||||
$('#send_but').addClass('displayNone'); //send button is hidden when not connected;
|
||||
$('#mes_continue').addClass('displayNone'); //continue button is hidden when not connected;
|
||||
$('#mes_impersonate').addClass('displayNone'); //continue button is hidden when not connected;
|
||||
@@ -326,7 +327,7 @@ function RA_checkOnlineStatus() {
|
||||
if (online_status !== undefined && online_status !== 'no_connection') {
|
||||
const send_textarea = $('#send_textarea');
|
||||
send_textarea.attr('placeholder', send_textarea.attr('connected_text')); //on connect, placeholder tells user to type message
|
||||
//$('#send_form').removeClass('no-connection');
|
||||
$('#send_form').removeClass('no-connection');
|
||||
$('#API-status-top').removeClass('fa-plug-circle-exclamation redOverlayGlow');
|
||||
$('#API-status-top').addClass('fa-plug');
|
||||
connection_made = true;
|
||||
@@ -389,6 +390,7 @@ function RA_autoconnect(PrevApi) {
|
||||
|| (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI)
|
||||
|| (secret_state[SECRET_KEYS.BLOCKENTROPY] && oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY)
|
||||
|| (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT)
|
||||
|| (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
|
||||
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
|
||||
) {
|
||||
$('#api_button_openai').trigger('click');
|
||||
@@ -748,8 +750,8 @@ export function initRossMods() {
|
||||
$(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
|
||||
|
||||
if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
|
||||
$(RightNavPanel).slideToggle(200, 'swing');
|
||||
$(RightNavDrawerIcon).toggleClass('openIcon closedIcon');
|
||||
slideToggle(RightNavPanel, getSlideToggleOptions());
|
||||
$(RightNavDrawerIcon).toggleClass('closedIcon openIcon');
|
||||
$(RightNavPanel).toggleClass('openDrawer closedDrawer');
|
||||
}
|
||||
}
|
||||
@@ -766,8 +768,8 @@ export function initRossMods() {
|
||||
$(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
|
||||
|
||||
if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
|
||||
$(LeftNavPanel).slideToggle(200, 'swing');
|
||||
$(LeftNavDrawerIcon).toggleClass('openIcon closedIcon');
|
||||
slideToggle(LeftNavPanel, getSlideToggleOptions());
|
||||
$(LeftNavDrawerIcon).toggleClass('closedIcon openIcon');
|
||||
$(LeftNavPanel).toggleClass('openDrawer closedDrawer');
|
||||
}
|
||||
}
|
||||
@@ -786,8 +788,8 @@ export function initRossMods() {
|
||||
|
||||
if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
|
||||
console.debug('closing WI after lock removal');
|
||||
$(WorldInfo).slideToggle(200, 'swing');
|
||||
$(WIDrawerIcon).toggleClass('openIcon closedIcon');
|
||||
slideToggle(WorldInfo, getSlideToggleOptions());
|
||||
$(WIDrawerIcon).toggleClass('closedIcon openIcon');
|
||||
$(WorldInfo).toggleClass('openDrawer closedDrawer');
|
||||
}
|
||||
}
|
||||
@@ -886,7 +888,40 @@ export function initRossMods() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
const cssAutofit = CSS.supports('field-sizing', 'content');
|
||||
|
||||
if (cssAutofit) {
|
||||
let lastHeight = chatBlock.offsetHeight;
|
||||
const chatBlockResizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.target !== chatBlock) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const threshold = 1;
|
||||
const newHeight = chatBlock.offsetHeight;
|
||||
const deltaHeight = newHeight - lastHeight;
|
||||
const isScrollAtBottom = Math.abs(chatBlock.scrollHeight - chatBlock.scrollTop - newHeight) <= threshold;
|
||||
|
||||
if (!isScrollAtBottom && Math.abs(deltaHeight) > threshold) {
|
||||
chatBlock.scrollTop -= deltaHeight;
|
||||
}
|
||||
lastHeight = newHeight;
|
||||
}
|
||||
});
|
||||
|
||||
chatBlockResizeObserver.observe(chatBlock);
|
||||
}
|
||||
|
||||
sendTextArea.addEventListener('input', () => {
|
||||
saveUserInputDebounced();
|
||||
|
||||
if (cssAutofit) {
|
||||
// Unset modifications made with a manual resize
|
||||
sendTextArea.style.height = 'auto';
|
||||
return;
|
||||
}
|
||||
|
||||
const hasContent = sendTextArea.value !== '';
|
||||
const fitsCurrentSize = sendTextArea.scrollHeight <= sendTextArea.offsetHeight;
|
||||
const isScrollbarShown = sendTextArea.clientWidth < sendTextArea.offsetWidth;
|
||||
@@ -894,7 +929,6 @@ export function initRossMods() {
|
||||
const needsDebounce = hasContent && (fitsCurrentSize || (isScrollbarShown && isHalfScreenHeight));
|
||||
if (needsDebounce) autoFitSendTextAreaDebounced();
|
||||
else autoFitSendTextArea();
|
||||
saveUserInputDebounced();
|
||||
});
|
||||
|
||||
restoreUserInput();
|
||||
|
@@ -440,7 +440,7 @@ async function onChatChanged() {
|
||||
const context = getContext();
|
||||
|
||||
// Disable the chara note if in a group
|
||||
$('#extension_floating_chara').prop('disabled', context.groupId ? true : false);
|
||||
$('#extension_floating_chara').prop('disabled', !!context.groupId);
|
||||
|
||||
const tokenCounter1 = chat_metadata[metadata_keys.prompt] ? await getTokenCountAsync(chat_metadata[metadata_keys.prompt]) : 0;
|
||||
$('#extension_floating_prompt_token_counter').text(tokenCounter1);
|
||||
|
@@ -12,6 +12,7 @@ const LIST_METADATA_KEY = 'chat_backgrounds';
|
||||
export let background_settings = {
|
||||
name: '__transparent.png',
|
||||
url: generateUrlParameter('__transparent.png', false),
|
||||
fitting: 'classic',
|
||||
};
|
||||
|
||||
export function loadBackgroundSettings(settings) {
|
||||
@@ -19,7 +20,12 @@ export function loadBackgroundSettings(settings) {
|
||||
if (!backgroundSettings || !backgroundSettings.name || !backgroundSettings.url) {
|
||||
backgroundSettings = background_settings;
|
||||
}
|
||||
if (!backgroundSettings.fitting) {
|
||||
backgroundSettings.fitting = 'classic';
|
||||
}
|
||||
setBackground(backgroundSettings.name, backgroundSettings.url);
|
||||
setFittingClass(backgroundSettings.fitting);
|
||||
$('#background_fitting').val(backgroundSettings.fitting);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,6 +339,14 @@ async function autoBackgroundCommand() {
|
||||
const bestMatch = fuse.search(reply, { limit: 1 });
|
||||
|
||||
if (bestMatch.length == 0) {
|
||||
for (const option of options) {
|
||||
if (String(reply).toLowerCase().includes(option.text.toLowerCase())) {
|
||||
console.debug('Fallback choosing background:', option);
|
||||
option.element.click();
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
toastr.warning('No match found. Please try again.');
|
||||
return '';
|
||||
}
|
||||
@@ -462,6 +476,18 @@ function highlightNewBackground(bg) {
|
||||
flashHighlight(newBg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fitting class for the background element
|
||||
* @param {string} fitting Fitting type
|
||||
*/
|
||||
function setFittingClass(fitting) {
|
||||
const backgrounds = $('#bg1, #bg_custom');
|
||||
backgrounds.toggleClass('cover', fitting === 'cover');
|
||||
backgrounds.toggleClass('contain', fitting === 'contain');
|
||||
backgrounds.toggleClass('stretch', fitting === 'stretch');
|
||||
backgrounds.toggleClass('center', fitting === 'center');
|
||||
}
|
||||
|
||||
function onBackgroundFilterInput() {
|
||||
const filterValue = String($(this).val()).toLowerCase();
|
||||
$('#bg_menu_content > div').each(function () {
|
||||
@@ -502,4 +528,9 @@ export function initBackgrounds() {
|
||||
helpString: 'Automatically changes the background based on the chat context using the AI request prompt',
|
||||
}));
|
||||
|
||||
$('#background_fitting').on('input', function () {
|
||||
background_settings.fitting = String($(this).val());
|
||||
setFittingClass(background_settings.fitting);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
}
|
||||
|
@@ -451,8 +451,14 @@ function getCustomSeparator() {
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the CFG prompt
|
||||
export function getCfgPrompt(guidanceScale, isNegative) {
|
||||
/**
|
||||
* Gets the CFG prompt based on the guidance scale.
|
||||
* @param {{type: number, value: number}} guidanceScale The CFG guidance scale
|
||||
* @param {boolean} isNegative Whether to get the negative prompt
|
||||
* @param {boolean} quiet Whether to suppress console output
|
||||
* @returns {{value: string, depth: number}} The CFG prompt and insertion depth
|
||||
*/
|
||||
export function getCfgPrompt(guidanceScale, isNegative, quiet = false) {
|
||||
let splitCfgPrompt = [];
|
||||
|
||||
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
|
||||
@@ -484,7 +490,7 @@ export function getCfgPrompt(guidanceScale, isNegative) {
|
||||
const customSeparator = getCustomSeparator();
|
||||
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
|
||||
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
|
||||
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||
!quiet && console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||
|
||||
return {
|
||||
value: combinedCfgPrompt,
|
||||
|
@@ -75,6 +75,7 @@
|
||||
* @property {string} [source_url] - The source URL associated with the character.
|
||||
* @property {{full_path: string}} [chub] - The Chub-specific data associated with the character.
|
||||
* @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character.
|
||||
* @property {{positive: string, negative: string}} [sd_character_prompt] - SD-specific data associated with the character.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@@ -53,6 +53,12 @@ const hash_derivations = {
|
||||
// command-r-08-2024
|
||||
'Command R'
|
||||
,
|
||||
|
||||
// Tulu
|
||||
'ac7498a36a719da630e99d48e6ebc4409de85a77556c2b6159eeb735bcbd11df':
|
||||
// Tulu-3-8B
|
||||
// Tulu-3-70B
|
||||
'Tulu'
|
||||
};
|
||||
|
||||
const substr_derivations = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -393,7 +393,7 @@ jQuery(async function () {
|
||||
const sendButton = $(`
|
||||
<div id="send_picture" class="list-group-item flex-container flexGap5">
|
||||
<div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
|
||||
Generate Caption
|
||||
<span data-i18n="Generate Caption">Generate Caption</span>
|
||||
</div>`);
|
||||
|
||||
$('#caption_wand_container').append(sendButton);
|
||||
|
@@ -53,6 +53,8 @@
|
||||
<option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option>
|
||||
<option data-type="anthropic" value="claude-3-haiku-20240307">claude-3-haiku-20240307</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-exp">gemini-2.0-flash-exp</option>
|
||||
<option data-type="google" value="gemini-2.0-flash-thinking-exp-1219">gemini-2.0-flash-thinking-exp-1219</option>
|
||||
<option data-type="google" value="gemini-1.5-flash">gemini-1.5-flash</option>
|
||||
<option data-type="google" value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
|
||||
<option data-type="google" value="gemini-1.5-flash-001">gemini-1.5-flash-001</option>
|
||||
@@ -62,13 +64,13 @@
|
||||
<option data-type="google" value="gemini-1.5-flash-8b-exp-0924">gemini-1.5-flash-8b-exp-0924</option>
|
||||
<option data-type="google" value="gemini-exp-1114">gemini-exp-1114</option>
|
||||
<option data-type="google" value="gemini-exp-1121">gemini-exp-1121</option>
|
||||
<option data-type="google" value="gemini-exp-1206">gemini-exp-1206</option>
|
||||
<option data-type="google" value="gemini-1.5-pro">gemini-1.5-pro</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-001">gemini-1.5-pro-001</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-002">gemini-1.5-pro-002</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-exp-0801">gemini-1.5-pro-exp-0801</option>
|
||||
<option data-type="google" value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
|
||||
<option data-type="google" value="gemini-pro-vision">gemini-pro-vision</option>
|
||||
<option data-type="groq" value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option>
|
||||
<option data-type="groq" value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option>
|
||||
<option data-type="groq" value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option>
|
||||
|
@@ -14,7 +14,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
|
||||
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
|
||||
import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
|
||||
import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js';
|
||||
import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'expressions';
|
||||
@@ -59,6 +59,7 @@ const EXPRESSION_API = {
|
||||
local: 0,
|
||||
extras: 1,
|
||||
llm: 2,
|
||||
webllm: 3,
|
||||
};
|
||||
|
||||
let expressionsList = null;
|
||||
@@ -697,6 +698,11 @@ async function moduleWorker() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If using LLM api then check if streamingProcessor is finished to avoid sending multiple requests to the API
|
||||
if (extension_settings.expressions.api === EXPRESSION_API.llm && context.streamingProcessor && !context.streamingProcessor.isFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
// API is busy
|
||||
if (inApiCall) {
|
||||
console.debug('Classification API is busy');
|
||||
@@ -847,7 +853,7 @@ function setTalkingHeadState(newState) {
|
||||
extension_settings.expressions.talkinghead = newState; // Store setting
|
||||
saveSettingsDebounced();
|
||||
|
||||
if (extension_settings.expressions.api == EXPRESSION_API.local || extension_settings.expressions.api == EXPRESSION_API.llm) {
|
||||
if ([EXPRESSION_API.local, EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -979,6 +985,71 @@ async function setSpriteSlashCommand(_, spriteId) {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sprite folder name (including override) for a character.
|
||||
* @param {object} char Character object
|
||||
* @param {string} char.avatar Avatar filename with extension
|
||||
* @returns {string} Sprite folder name
|
||||
* @throws {Error} If character not found or avatar not set
|
||||
*/
|
||||
function spriteFolderNameFromCharacter(char) {
|
||||
const avatarFileName = char.avatar.replace(/\.[^/.]+$/, '');
|
||||
const expressionOverride = extension_settings.expressionOverrides.find(e => e.name === avatarFileName);
|
||||
return expressionOverride?.path ? expressionOverride.path : avatarFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slash command callback for /uploadsprite
|
||||
*
|
||||
* label= is required
|
||||
* if name= is provided, it will be used as a findChar lookup
|
||||
* if name= is not provided, the last character's name will be used
|
||||
* if folder= is a full path, it will be used as the folder
|
||||
* if folder= is a partial path, it will be appended to the character's name
|
||||
* if folder= is not provided, the character's override folder will be used, if set
|
||||
*
|
||||
* @param {object} args
|
||||
* @param {string} args.name Character name or avatar key, passed through findChar
|
||||
* @param {string} args.label Expression label
|
||||
* @param {string} args.folder Sprite folder path, processed using backslash rules
|
||||
* @param {string} imageUrl Image URI to fetch and upload
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function uploadSpriteCommand({ name, label, folder }, imageUrl) {
|
||||
if (!imageUrl) throw new Error('Image URL is required');
|
||||
if (!label || typeof label !== 'string') throw new Error('Expression label is required');
|
||||
|
||||
label = label.replace(/[^a-z]/gi, '').toLowerCase().trim();
|
||||
if (!label) throw new Error('Expression label must contain at least one letter');
|
||||
|
||||
name = name || getLastCharacterMessage().original_avatar || getLastCharacterMessage().name;
|
||||
const char = findChar({ name });
|
||||
|
||||
if (!folder) {
|
||||
folder = spriteFolderNameFromCharacter(char);
|
||||
} else if (folder.startsWith('/') || folder.startsWith('\\')) {
|
||||
const subfolder = folder.slice(1);
|
||||
folder = `${char.name}/${subfolder}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(imageUrl);
|
||||
const blob = await response.blob();
|
||||
const file = new File([blob], 'image.png', { type: 'image/png' });
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('name', folder); // this is the folder or character name
|
||||
formData.append('label', label); // this is the expression label
|
||||
formData.append('avatar', file); // this is the image file
|
||||
|
||||
await handleFileUpload('/api/sprites/upload', formData);
|
||||
console.debug(`[${MODULE_NAME}] Upload of ${imageUrl} completed for ${name} with label ${label}`);
|
||||
} catch (error) {
|
||||
console.error(`[${MODULE_NAME}] Error uploading file:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the classification text to reduce the amount of text sent to the API.
|
||||
* Quotes and asterisks are to be removed. If the text is less than 300 characters, it is returned as is.
|
||||
@@ -995,6 +1066,11 @@ function sampleClassifyText(text) {
|
||||
// Replace macros, remove asterisks and quotes
|
||||
let result = substituteParams(text).replace(/[*"]/g, '');
|
||||
|
||||
// If using LLM api there is no need to check length of characters
|
||||
if (extension_settings.expressions.api === EXPRESSION_API.llm) {
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
const SAMPLE_THRESHOLD = 500;
|
||||
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
|
||||
|
||||
@@ -1047,11 +1123,39 @@ function parseLlmResponse(emotionResponse, labels) {
|
||||
console.debug(`fuzzy search found: ${result[0].item} as closest for the LLM response:`, emotionResponse);
|
||||
return result[0].item;
|
||||
}
|
||||
const lowerCaseResponse = String(emotionResponse || '').toLowerCase();
|
||||
for (const label of labels) {
|
||||
if (lowerCaseResponse.includes(label.toLowerCase())) {
|
||||
console.debug(`Found label ${label} in the LLM response:`, emotionResponse);
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Could not parse emotion response ' + emotionResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON schema for the LLM API.
|
||||
* @param {string[]} emotions A list of emotions to search for.
|
||||
* @returns {object} The JSON schema for the LLM API.
|
||||
*/
|
||||
function getJsonSchema(emotions) {
|
||||
return {
|
||||
$schema: 'http://json-schema.org/draft-04/schema#',
|
||||
type: 'object',
|
||||
properties: {
|
||||
emotion: {
|
||||
type: 'string',
|
||||
enum: emotions,
|
||||
},
|
||||
},
|
||||
required: [
|
||||
'emotion',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function onTextGenSettingsReady(args) {
|
||||
// Only call if inside an API call
|
||||
if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) {
|
||||
@@ -1061,19 +1165,7 @@ function onTextGenSettingsReady(args) {
|
||||
stop: [],
|
||||
stopping_strings: [],
|
||||
custom_token_bans: [],
|
||||
json_schema: {
|
||||
$schema: 'http://json-schema.org/draft-04/schema#',
|
||||
type: 'object',
|
||||
properties: {
|
||||
emotion: {
|
||||
type: 'string',
|
||||
enum: emotions,
|
||||
},
|
||||
},
|
||||
required: [
|
||||
'emotion',
|
||||
],
|
||||
},
|
||||
json_schema: getJsonSchema(emotions),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1129,6 +1221,22 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin
|
||||
const emotionResponse = await generateRaw(text, main_api, false, false, prompt);
|
||||
return parseLlmResponse(emotionResponse, expressionsList);
|
||||
}
|
||||
// Using WebLLM
|
||||
case EXPRESSION_API.webllm: {
|
||||
if (!isWebLlmSupported()) {
|
||||
console.warn('WebLLM is not supported. Using fallback expression');
|
||||
return getFallbackExpression();
|
||||
}
|
||||
|
||||
const expressionsList = await getExpressionsList();
|
||||
const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList);
|
||||
const messages = [
|
||||
{ role: 'user', content: text + '\n\n' + prompt },
|
||||
];
|
||||
|
||||
const emotionResponse = await generateWebLlmChatPrompt(messages);
|
||||
return parseLlmResponse(emotionResponse, expressionsList);
|
||||
}
|
||||
// Extras
|
||||
default: {
|
||||
const url = new URL(getApiUrl());
|
||||
@@ -1239,8 +1347,6 @@ async function drawSpritesList(character, labels, sprites) {
|
||||
* @returns {Promise<string>} Rendered list item template
|
||||
*/
|
||||
async function getListItem(item, imageSrc, textClass, isCustom) {
|
||||
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
imageSrc = isFirefox ? `${imageSrc}?t=${Date.now()}` : imageSrc;
|
||||
return renderExtensionTemplateAsync(MODULE_NAME, 'list-item', { item, imageSrc, textClass, isCustom });
|
||||
}
|
||||
|
||||
@@ -1593,7 +1699,7 @@ function onExpressionApiChanged() {
|
||||
const tempApi = this.value;
|
||||
if (tempApi) {
|
||||
extension_settings.expressions.api = Number(tempApi);
|
||||
$('.expression_llm_prompt_block').toggle(extension_settings.expressions.api === EXPRESSION_API.llm);
|
||||
$('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api));
|
||||
expressionsList = null;
|
||||
spriteCache = {};
|
||||
moduleWorker();
|
||||
@@ -1930,7 +2036,7 @@ function migrateSettings() {
|
||||
|
||||
await renderAdditionalExpressionSettings();
|
||||
$('#expression_api').val(extension_settings.expressions.api ?? EXPRESSION_API.extras);
|
||||
$('.expression_llm_prompt_block').toggle(extension_settings.expressions.api === EXPRESSION_API.llm);
|
||||
$('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api));
|
||||
$('#expression_llm_prompt').val(extension_settings.expressions.llmPrompt ?? '');
|
||||
$('#expression_llm_prompt').on('input', function () {
|
||||
extension_settings.expressions.llmPrompt = $(this).val();
|
||||
@@ -2173,4 +2279,43 @@ function migrateSettings() {
|
||||
</div>
|
||||
`,
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'uploadsprite',
|
||||
callback: async (args, url) => {
|
||||
await uploadSpriteCommand(args, url);
|
||||
return '';
|
||||
},
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'URL of the image to upload',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
}),
|
||||
],
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
description: 'Character name or avatar key (default is current character)',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: false,
|
||||
acceptsMultiple: false,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'label',
|
||||
description: 'Sprite label/expression name',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: localEnumProviders.expressions,
|
||||
isRequired: true,
|
||||
acceptsMultiple: false,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'folder',
|
||||
description: 'Override folder to upload into',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: false,
|
||||
acceptsMultiple: false,
|
||||
}),
|
||||
],
|
||||
helpString: '<div>Upload a sprite from a URL.</div><div>Example:</div><pre><code>/uploadsprite name=Seraphina label=joy /user/images/Seraphina/Seraphina_2024-12-22@12h37m57s.png</code></pre>',
|
||||
}));
|
||||
})();
|
||||
|
@@ -24,7 +24,8 @@
|
||||
<select id="expression_api" class="flex1 margin0">
|
||||
<option value="0" data-i18n="Local">Local</option>
|
||||
<option value="1" data-i18n="Extras">Extras</option>
|
||||
<option value="2" data-i18n="LLM">LLM</option>
|
||||
<option value="2" data-i18n="Main API">Main API</option>
|
||||
<option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="expression_llm_prompt_block m-b-1 m-t-1">
|
||||
@@ -40,7 +41,7 @@
|
||||
<div class="expression_fallback_block m-b-1 m-t-1">
|
||||
<label for="expression_fallback" data-i18n="Default / Fallback Expression">Default / Fallback Expression</label>
|
||||
<small data-i18n="Set the default and fallback expression being used when no matching expression is found.">Set the default and fallback expression being used when no matching expression is found.</small>
|
||||
<select id="expression_fallback" class="flex1 margin0" data-i18n="Fallback Expression" placeholder="Fallback Expression"></select>
|
||||
<select id="expression_fallback" class="flex1 margin0"></select>
|
||||
</div>
|
||||
<div class="expression_custom_block m-b-1 m-t-1">
|
||||
<label for="expression_custom" data-i18n="Custom Expressions">Custom Expressions</label>
|
||||
|
@@ -419,30 +419,35 @@ export class SlashCommandHandler {
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'set',
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to add the context menu to',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'label',
|
||||
description: 'Quick Reply label',
|
||||
description: 'Label of Quick Reply to add the context menu to',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: localEnumProviders.qrEntries,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'id',
|
||||
description: 'numeric ID of the QR, e.g., id=42',
|
||||
description: 'Numeric ID of Quick Reply to add the context menu to, e.g. id=42',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: localEnumProviders.qrIds,
|
||||
}),
|
||||
new SlashCommandNamedArgument(
|
||||
'chain', 'boolean', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
|
||||
'chain',
|
||||
'If true, button QR is sent together with (before) the clicked QR from the context menu',
|
||||
[ARGUMENT_TYPE.BOOLEAN],
|
||||
false,
|
||||
false,
|
||||
'false',
|
||||
),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to add as a context menu',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
@@ -450,13 +455,16 @@ export class SlashCommandHandler {
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Add context menu preset to a QR.
|
||||
Add a context menu preset to a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset</code></pre>
|
||||
<pre><code>/qr-contextadd set=MyQRSetWithTheButton label=MyButton chain=true MyQRSetWithContextItems</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -470,27 +478,27 @@ export class SlashCommandHandler {
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'set',
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to remove the context menu from',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'label',
|
||||
description: 'Quick Reply label',
|
||||
description: 'Label of Quick Reply to remove the context menu from',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumProvider: localEnumProviders.qrEntries,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'id',
|
||||
description: 'numeric ID of the QR, e.g., id=42',
|
||||
description: 'Numeric ID of Quick Reply to remove the context menu from, e.g. id=42',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: localEnumProviders.qrIds,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
SlashCommandArgument.fromProps({
|
||||
description: 'QR set name',
|
||||
description: 'Name of QR set to remove',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
enumProvider: localEnumProviders.qrSets,
|
||||
@@ -500,6 +508,9 @@ export class SlashCommandHandler {
|
||||
<div>
|
||||
Remove context menu preset from a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and <code>label</code> are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
@@ -541,6 +552,9 @@ export class SlashCommandHandler {
|
||||
<div>
|
||||
Remove all context menu presets from a QR.
|
||||
</div>
|
||||
<div>
|
||||
If <code>id</code> and a label are both provided, <code>id</code> will be used.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<ul>
|
||||
@@ -908,12 +922,11 @@ export class SlashCommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
createContextItem(args, name) {
|
||||
try {
|
||||
this.api.createContextItem(
|
||||
args.set,
|
||||
args.label,
|
||||
args.id !== undefined ? Number(args.id) : args.label,
|
||||
name,
|
||||
isTrueBoolean(args.chain),
|
||||
);
|
||||
@@ -923,14 +936,14 @@ export class SlashCommandHandler {
|
||||
}
|
||||
deleteContextItem(args, name) {
|
||||
try {
|
||||
this.api.deleteContextItem(args.set, args.label, name);
|
||||
this.api.deleteContextItem(args.set, args.id !== undefined ? Number(args.id) : args.label, name);
|
||||
} catch (ex) {
|
||||
toastr.error(ex.message);
|
||||
}
|
||||
}
|
||||
clearContextMenu(args, label) {
|
||||
try {
|
||||
this.api.clearContextMenu(args.set, args.label ?? label);
|
||||
this.api.clearContextMenu(args.set, args.id !== undefined ? Number(args.id) : args.label ?? label);
|
||||
} catch (ex) {
|
||||
toastr.error(ex.message);
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ export class ContextMenu {
|
||||
this.itemList = this.build(qr).children;
|
||||
this.itemList.forEach(item => {
|
||||
item.onExpand = () => {
|
||||
this.itemList.filter(it => it != item)
|
||||
this.itemList.filter(it => it !== item)
|
||||
.forEach(it => it.collapse());
|
||||
};
|
||||
});
|
||||
@@ -36,6 +36,7 @@ export class ContextMenu {
|
||||
icon: qr.icon,
|
||||
showLabel: qr.showLabel,
|
||||
label: qr.label,
|
||||
title: qr.title,
|
||||
message: (chainedMessage && qr.message ? `${chainedMessage} | ` : '') + qr.message,
|
||||
children: [],
|
||||
};
|
||||
@@ -45,12 +46,29 @@ export class ContextMenu {
|
||||
const nextHierarchy = [...hierarchy, cl.set];
|
||||
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
||||
tree.children.push(new MenuHeader(cl.set.name));
|
||||
cl.set.qrList.forEach(subQr => {
|
||||
|
||||
// If the Quick Reply's own set is added as a context menu,
|
||||
// show only the sub-QRs that are Invisible but have an icon
|
||||
// intent: allow a QR set to be assigned to one of its own QR buttons for a "burger" menu
|
||||
// with "UI" QRs either in the bar or in the menu, and "library function" QRs still hidden.
|
||||
// - QRs already visible on the bar are filtered out,
|
||||
// - hidden QRs without an icon are filtered out,
|
||||
// - hidden QRs **with an icon** are shown in the menu
|
||||
// so everybody is happy
|
||||
const qrsOwnSetAddedAsContextMenu = cl.set.qrList.includes(qr);
|
||||
const visible = (subQr) => {
|
||||
return qrsOwnSetAddedAsContextMenu
|
||||
? subQr.isHidden && !!subQr.icon // yes .isHidden gets inverted here
|
||||
: !subQr.isHidden;
|
||||
};
|
||||
|
||||
cl.set.qrList.filter(visible).forEach(subQr => {
|
||||
const subTree = this.build(subQr, cl.isChained ? tree.message : null, nextHierarchy, nextLabelHierarchy);
|
||||
tree.children.push(new MenuItem(
|
||||
subTree.icon,
|
||||
subTree.showLabel,
|
||||
subTree.label,
|
||||
subTree.title,
|
||||
subTree.message,
|
||||
(evt) => {
|
||||
evt.stopPropagation();
|
||||
|
@@ -2,7 +2,7 @@ import { MenuItem } from './MenuItem.js';
|
||||
|
||||
export class MenuHeader extends MenuItem {
|
||||
constructor(/**@type {String}*/label) {
|
||||
super(null, null, label, null, null);
|
||||
super(null, null, label, null, null, null, []);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -4,11 +4,11 @@ export class MenuItem {
|
||||
/**@type {string}*/ icon;
|
||||
/**@type {boolean}*/ showLabel;
|
||||
/**@type {string}*/ label;
|
||||
/**@type {string}*/ title;
|
||||
/**@type {object}*/ value;
|
||||
/**@type {function}*/ callback;
|
||||
/**@type {MenuItem[]}*/ childList = [];
|
||||
/**@type {SubMenu}*/ subMenu;
|
||||
/**@type {boolean}*/ isForceExpanded = false;
|
||||
|
||||
/**@type {HTMLElement}*/ root;
|
||||
|
||||
@@ -19,17 +19,19 @@ export class MenuItem {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} icon
|
||||
* @param {boolean} showLabel
|
||||
* @param {?string} icon
|
||||
* @param {?boolean} showLabel
|
||||
* @param {string} label
|
||||
* @param {?string} title Tooltip
|
||||
* @param {object} value
|
||||
* @param {function} callback
|
||||
* @param {MenuItem[]} children
|
||||
*/
|
||||
constructor(icon, showLabel, label, value, callback, children = []) {
|
||||
constructor(icon, showLabel, label, title, value, callback, children = []) {
|
||||
this.icon = icon;
|
||||
this.showLabel = showLabel;
|
||||
this.label = label;
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
this.callback = callback;
|
||||
this.childList = children;
|
||||
@@ -42,12 +44,15 @@ export class MenuItem {
|
||||
this.root = item;
|
||||
item.classList.add('list-group-item');
|
||||
item.classList.add('ctx-item');
|
||||
item.title = this.value;
|
||||
|
||||
// if a title/tooltip is set, add it, otherwise use the QR content
|
||||
// same as for the main QR list
|
||||
item.title = this.title || this.value;
|
||||
|
||||
if (this.callback) {
|
||||
item.addEventListener('click', (evt) => this.callback(evt, this));
|
||||
}
|
||||
const icon = document.createElement('div'); {
|
||||
this.domIcon = icon;
|
||||
icon.classList.add('qr--button-icon');
|
||||
icon.classList.add('fa-solid');
|
||||
if (!this.icon) icon.classList.add('qr--hidden');
|
||||
@@ -55,7 +60,6 @@ export class MenuItem {
|
||||
item.append(icon);
|
||||
}
|
||||
const lbl = document.createElement('div'); {
|
||||
this.domLabel = lbl;
|
||||
lbl.classList.add('qr--button-label');
|
||||
if (this.icon && !this.showLabel) lbl.classList.add('qr--hidden');
|
||||
lbl.textContent = this.label;
|
||||
|
@@ -174,6 +174,9 @@
|
||||
position: absolute;
|
||||
overflow: visible;
|
||||
}
|
||||
.ctx-menu .ctx-item .qr--hidden {
|
||||
display: none;
|
||||
}
|
||||
.list-group .list-group-item.ctx-header {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
|
@@ -176,6 +176,10 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ctx-menu .ctx-item .qr--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-group .list-group-item.ctx-header {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<div id="sd_gen" class="list-group-item flex-container flexGap5">
|
||||
<div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion" data-i18n="[title]Trigger Stable Diffusion"></div>
|
||||
<span>Generate Image</span>
|
||||
<span data-i18n="Generate Image">Generate Image</span>
|
||||
</div>
|
||||
<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
|
||||
<div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task" data-i18n="[title]Abort current image generation task"></div>
|
||||
<span>Stop Image Generation</span>
|
||||
<span data-i18n="Stop Image Generation">Stop Image Generation</span>
|
||||
</div>
|
||||
|
@@ -60,7 +60,6 @@ import { ToolManager } from '../../tool-calling.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'sd';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
// This is a 1x1 transparent PNG
|
||||
const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
||||
const CUSTOM_STOP_EVENT = 'sd_stop_generation';
|
||||
@@ -219,7 +218,7 @@ const defaultSettings = {
|
||||
// CFG Scale
|
||||
scale_min: 1,
|
||||
scale_max: 30,
|
||||
scale_step: 0.5,
|
||||
scale_step: 0.1,
|
||||
scale: 7,
|
||||
|
||||
// Sampler steps
|
||||
@@ -320,6 +319,7 @@ const defaultSettings = {
|
||||
wand_visible: false,
|
||||
command_visible: false,
|
||||
interactive_visible: false,
|
||||
tool_visible: false,
|
||||
|
||||
// Stability AI settings
|
||||
stability_style_preset: 'anime',
|
||||
@@ -489,6 +489,7 @@ async function loadSettings() {
|
||||
$('#sd_wand_visible').prop('checked', extension_settings.sd.wand_visible);
|
||||
$('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
|
||||
$('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
|
||||
$('#sd_tool_visible').prop('checked', extension_settings.sd.tool_visible);
|
||||
$('#sd_stability_style_preset').val(extension_settings.sd.stability_style_preset);
|
||||
$('#sd_huggingface_model_id').val(extension_settings.sd.huggingface_model_id);
|
||||
$('#sd_function_tool').prop('checked', extension_settings.sd.function_tool);
|
||||
@@ -845,6 +846,11 @@ function onInteractiveVisibleInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onToolVisibleInput() {
|
||||
extension_settings.sd.tool_visible = !!$('#sd_tool_visible').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onClipSkipInput() {
|
||||
extension_settings.sd.clip_skip = Number($('#sd_clip_skip').val());
|
||||
$('#sd_clip_skip_value').val(extension_settings.sd.clip_skip);
|
||||
@@ -1105,7 +1111,8 @@ function onHrSecondPassStepsInput() {
|
||||
}
|
||||
|
||||
function onComfyUrlInput() {
|
||||
extension_settings.sd.comfy_url = $('#sd_comfy_url').val();
|
||||
// Remove trailing slashes
|
||||
extension_settings.sd.comfy_url = String($('#sd_comfy_url').val());
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@@ -1606,17 +1613,12 @@ async function loadVladSamplers() {
|
||||
}
|
||||
|
||||
async function loadNovelSamplers() {
|
||||
if (!secret_state[SECRET_KEYS.NOVEL]) {
|
||||
console.debug('NovelAI API key is not set.');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'k_euler_ancestral',
|
||||
'k_euler',
|
||||
'k_dpmpp_2m',
|
||||
'k_dpmpp_sde',
|
||||
'k_dpmpp_2s_ancestral',
|
||||
'k_euler',
|
||||
'k_euler_ancestral',
|
||||
'k_dpm_fast',
|
||||
'ddim',
|
||||
];
|
||||
@@ -1972,12 +1974,11 @@ async function loadVladModels() {
|
||||
}
|
||||
|
||||
async function loadNovelModels() {
|
||||
if (!secret_state[SECRET_KEYS.NOVEL]) {
|
||||
console.debug('NovelAI API key is not set.');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
value: 'nai-diffusion-4-curated-preview',
|
||||
text: 'NAI Diffusion Anime V4 (Curated Preview)',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion-3',
|
||||
text: 'NAI Diffusion Anime V3',
|
||||
@@ -1986,22 +1987,10 @@ async function loadNovelModels() {
|
||||
value: 'nai-diffusion-2',
|
||||
text: 'NAI Diffusion Anime V2',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion',
|
||||
text: 'NAI Diffusion Anime V1 (Full)',
|
||||
},
|
||||
{
|
||||
value: 'safe-diffusion',
|
||||
text: 'NAI Diffusion Anime V1 (Curated)',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion-furry-3',
|
||||
text: 'NAI Diffusion Furry V3',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion-furry',
|
||||
text: 'NAI Diffusion Furry',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2042,7 +2031,7 @@ async function loadSchedulers() {
|
||||
schedulers = await getAutoRemoteSchedulers();
|
||||
break;
|
||||
case sources.novel:
|
||||
schedulers = ['N/A'];
|
||||
schedulers = ['karras', 'native', 'exponential', 'polyexponential'];
|
||||
break;
|
||||
case sources.vlad:
|
||||
schedulers = ['N/A'];
|
||||
@@ -3042,12 +3031,14 @@ async function generateAutoImage(prompt, negativePrompt, signal) {
|
||||
enable_hr: !!extension_settings.sd.enable_hr,
|
||||
hr_upscaler: extension_settings.sd.hr_upscaler,
|
||||
hr_scale: extension_settings.sd.hr_scale,
|
||||
hr_additional_modules: [],
|
||||
denoising_strength: extension_settings.sd.denoising_strength,
|
||||
hr_second_pass_steps: extension_settings.sd.hr_second_pass_steps,
|
||||
seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
|
||||
override_settings: {
|
||||
CLIP_stop_at_last_layers: extension_settings.sd.clip_skip,
|
||||
sd_vae: isValidVae ? extension_settings.sd.vae : undefined,
|
||||
forge_additional_modules: isValidVae ? [extension_settings.sd.vae] : undefined, // For SD Forge
|
||||
},
|
||||
override_settings_restore_afterwards: true,
|
||||
clip_skip: extension_settings.sd.clip_skip, // For SD.Next
|
||||
@@ -3151,6 +3142,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
|
||||
prompt: prompt,
|
||||
model: extension_settings.sd.model,
|
||||
sampler: extension_settings.sd.sampler,
|
||||
scheduler: extension_settings.sd.scheduler,
|
||||
steps: steps,
|
||||
scale: extension_settings.sd.scale,
|
||||
width: width,
|
||||
@@ -3178,13 +3170,18 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
|
||||
* @returns {{steps: number, width: number, height: number, sm: boolean, sm_dyn: boolean}} - A tuple of parameters for NovelAI API.
|
||||
*/
|
||||
function getNovelParams() {
|
||||
let steps = extension_settings.sd.steps;
|
||||
let steps = Math.min(extension_settings.sd.steps, 50);
|
||||
let width = extension_settings.sd.width;
|
||||
let height = extension_settings.sd.height;
|
||||
let sm = extension_settings.sd.novel_sm;
|
||||
let sm_dyn = extension_settings.sd.novel_sm_dyn;
|
||||
|
||||
if (extension_settings.sd.sampler === 'ddim') {
|
||||
// If a source was never changed after the scheduler setting was added, we need to set it to 'karras' for compatibility.
|
||||
if (!extension_settings.sd.scheduler || extension_settings.sd.scheduler === 'normal') {
|
||||
extension_settings.sd.scheduler = 'karras';
|
||||
}
|
||||
|
||||
if (extension_settings.sd.sampler === 'ddim' || extension_settings.sd.model === 'nai-diffusion-4-curated-preview') {
|
||||
sm = false;
|
||||
sm_dyn = false;
|
||||
}
|
||||
@@ -3315,7 +3312,6 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
|
||||
'scale',
|
||||
'width',
|
||||
'height',
|
||||
'clip_skip',
|
||||
];
|
||||
|
||||
const workflowResponse = await fetch('/api/sd/comfy/workflow', {
|
||||
@@ -3338,6 +3334,9 @@ async function generateComfyImage(prompt, negativePrompt, signal) {
|
||||
const denoising_strength = extension_settings.sd.denoising_strength === undefined ? 1.0 : extension_settings.sd.denoising_strength;
|
||||
workflow = workflow.replaceAll('"%denoise%"', JSON.stringify(denoising_strength));
|
||||
|
||||
const clip_skip = isNaN(extension_settings.sd.clip_skip) ? -1 : -extension_settings.sd.clip_skip;
|
||||
workflow = workflow.replaceAll('"%clip_skip%"', JSON.stringify(clip_skip));
|
||||
|
||||
placeholders.forEach(ph => {
|
||||
workflow = workflow.replaceAll(`"%${ph}%"`, JSON.stringify(extension_settings.sd[ph]));
|
||||
});
|
||||
@@ -3671,6 +3670,8 @@ function getVisibilityByInitiator(initiator) {
|
||||
return !!extension_settings.sd.wand_visible;
|
||||
case initiators.command:
|
||||
return !!extension_settings.sd.command_visible;
|
||||
case initiators.tool:
|
||||
return !!extension_settings.sd.tool_visible;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -3687,8 +3688,6 @@ async function addSDGenButtons() {
|
||||
const button = $('#sd_gen');
|
||||
const dropdown = $('#sd_dropdown');
|
||||
dropdown.hide();
|
||||
button.hide();
|
||||
messageButton.hide();
|
||||
|
||||
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
|
||||
placement: 'top',
|
||||
@@ -3770,18 +3769,6 @@ function isValidState() {
|
||||
}
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
if (isValidState()) {
|
||||
$('#sd_gen').show();
|
||||
$('.sd_message_gen').show();
|
||||
}
|
||||
else {
|
||||
$('#sd_gen').hide();
|
||||
$('.sd_message_gen').hide();
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
let buttonAbortController = null;
|
||||
|
||||
async function sdMessageButton(e) {
|
||||
@@ -4432,6 +4419,7 @@ jQuery(async () => {
|
||||
$('#sd_wand_visible').on('input', onWandVisibleInput);
|
||||
$('#sd_command_visible').on('input', onCommandVisibleInput);
|
||||
$('#sd_interactive_visible').on('input', onInteractiveVisibleInput);
|
||||
$('#sd_tool_visible').on('input', onToolVisibleInput);
|
||||
$('#sd_swap_dimensions').on('click', onSwapDimensionsClick);
|
||||
$('#sd_stability_key').on('click', onStabilityKeyClick);
|
||||
$('#sd_stability_style_preset').on('change', onStabilityStylePresetChange);
|
||||
|
@@ -109,7 +109,7 @@
|
||||
<i data-i18n="The server must be accessible from the SillyTavern host machine.">The server must be accessible from the SillyTavern host machine.</i>
|
||||
</div>
|
||||
<div data-sd-source="horde">
|
||||
<i data-i18n="Hint: Save an API key in Horde KoboldAI API settings to use it here.">Hint: Save an API key in Horde KoboldAI API settings to use it here.</i>
|
||||
<i data-i18n="Hint: Save an API key in AI Horde API settings to use it here.">Hint: Save an API key in AI Horde API settings to use it here.</i>
|
||||
<label for="sd_horde_nsfw" class="checkbox_label">
|
||||
<input id="sd_horde_nsfw" type="checkbox" />
|
||||
<span data-i18n="Allow NSFW images from Horde">
|
||||
@@ -274,7 +274,7 @@
|
||||
<select id="sd_sampler"></select>
|
||||
</div>
|
||||
|
||||
<div class="flex1" data-sd-source="comfy,auto">
|
||||
<div class="flex1" data-sd-source="comfy,auto,novel">
|
||||
<label for="sd_scheduler" data-i18n="Scheduler">Scheduler</label>
|
||||
<select id="sd_scheduler"></select>
|
||||
</div>
|
||||
@@ -459,25 +459,32 @@
|
||||
<div class="flex-container flexFlowColumn marginTopBot5 flexGap10">
|
||||
<label for="sd_wand_visible" class="checkbox_label">
|
||||
<span class="flex1 flex-container alignItemsCenter">
|
||||
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
||||
<i class="fa-solid fa-wand-magic-sparkles fa-fw"></i>
|
||||
<span data-i18n="Extensions Menu">Extensions Menu</span>
|
||||
</span>
|
||||
<input id="sd_wand_visible" type="checkbox" />
|
||||
</label>
|
||||
<label for="sd_command_visible" class="checkbox_label">
|
||||
<span class="flex1 flex-container alignItemsCenter">
|
||||
<i class="fa-solid fa-terminal"></i>
|
||||
<i class="fa-solid fa-terminal fa-fw"></i>
|
||||
<span data-i18n="Slash Command">Slash Command</span>
|
||||
</span>
|
||||
<input id="sd_command_visible" type="checkbox" />
|
||||
</label>
|
||||
<label for="sd_interactive_visible" class="checkbox_label">
|
||||
<span class="flex1 flex-container alignItemsCenter">
|
||||
<i class="fa-solid fa-message"></i>
|
||||
<i class="fa-solid fa-message fa-fw"></i>
|
||||
<span data-i18n="Interactive Mode">Interactive Mode</span>
|
||||
</span>
|
||||
<input id="sd_interactive_visible" type="checkbox" />
|
||||
</label>
|
||||
<label for="sd_tool_visible" class="checkbox_label">
|
||||
<span class="flex1 flex-container alignItemsCenter">
|
||||
<i class="fa-solid fa-wrench fa-fw"></i>
|
||||
<span data-i18n="Function Tool">Function Tool</span>
|
||||
</span>
|
||||
<input id="sd_tool_visible" type="checkbox" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
#sd_dropdown {
|
||||
z-index: 30000;
|
||||
backdrop-filter: blur(--SmartThemeBlurStrength);
|
||||
backdrop-filter: blur(var(--SmartThemeBlurStrength));
|
||||
}
|
||||
|
||||
#sd_comfy_open_workflow_editor {
|
||||
|
0
public/scripts/extensions/third-party/.gitkeep
vendored
Normal file
0
public/scripts/extensions/third-party/.gitkeep
vendored
Normal file
@@ -14,16 +14,20 @@
|
||||
</select>
|
||||
<label data-i18n="ext_translate_mode_provider" for="translation_provider">Provider</label>
|
||||
<div class="flex-container gap5px flexnowrap marginBot5">
|
||||
<select id="translation_provider" name="provider" class="margin0">
|
||||
<option value="libre">Libre</option>
|
||||
<select id="translation_provider" name="provider" class="margin0 text_pole flex2">
|
||||
<option value="libre">LibreTranslate</option>
|
||||
<option value="google">Google</option>
|
||||
<option value="lingva">Lingva</option>
|
||||
<option value="deepl">DeepL</option>
|
||||
<option value="deepl">DeepL API</option>
|
||||
<option value="deeplx">DeepLX</option>
|
||||
<option value="bing">Bing</option>
|
||||
<option value="oneringtranslator">OneRingTranslator</option>
|
||||
<option value="yandex">Yandex</option>
|
||||
<select>
|
||||
<select id="deepl_api_endpoint" class="margin0 text_pole flex1" title="DeepL API Endpoint">
|
||||
<option value="free">Free</option>
|
||||
<option value="pro">Pro</option>
|
||||
</select>
|
||||
<div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
|
||||
<div id="translate_url_button" class="menu_button fa-solid fa-link margin0"></div>
|
||||
</div>
|
||||
@@ -35,4 +39,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -14,6 +14,8 @@ import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from '../../popup.js';
|
||||
import { findSecret, secret_state, writeSecret } from '../../secrets.js';
|
||||
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
|
||||
import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { enumTypes, SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
|
||||
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
|
||||
import { splitRecursive } from '../../utils.js';
|
||||
|
||||
@@ -32,6 +34,7 @@ const defaultSettings = {
|
||||
internal_language: 'en',
|
||||
provider: 'google',
|
||||
auto_mode: autoModeOptions.NONE,
|
||||
deepl_endpoint: 'free',
|
||||
};
|
||||
|
||||
const languageCodes = {
|
||||
@@ -106,7 +109,8 @@ const languageCodes = {
|
||||
'Pashto': 'ps',
|
||||
'Persian': 'fa',
|
||||
'Polish': 'pl',
|
||||
'Portuguese (Portugal, Brazil)': 'pt',
|
||||
'Portuguese (Portugal)': 'pt-PT',
|
||||
'Portuguese (Brazil)': 'pt-BR',
|
||||
'Punjabi': 'pa',
|
||||
'Romanian': 'ro',
|
||||
'Russian': 'ru',
|
||||
@@ -151,6 +155,7 @@ function showKeysButton() {
|
||||
$('#translate_key_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider]));
|
||||
$('#translate_url_button').toggle(providerOptionalUrl);
|
||||
$('#translate_url_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider + '_url']));
|
||||
$('#deepl_api_endpoint').toggle(extension_settings.translate.provider === 'deepl');
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
@@ -160,9 +165,10 @@ function loadSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true);
|
||||
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true);
|
||||
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', true);
|
||||
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', 'true');
|
||||
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', 'true');
|
||||
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', 'true');
|
||||
$('#deepl_api_endpoint').val(extension_settings.translate.deepl_endpoint).toggle(extension_settings.translate.provider === 'deepl');
|
||||
showKeysButton();
|
||||
}
|
||||
|
||||
@@ -284,10 +290,11 @@ async function translateProviderDeepl(text, lang) {
|
||||
throw new Error('No DeepL API key');
|
||||
}
|
||||
|
||||
const endpoint = extension_settings.translate.deepl_endpoint || 'free';
|
||||
const response = await fetch('/api/translate/deepl', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ text: text, lang: lang }),
|
||||
body: JSON.stringify({ text: text, lang: lang, endpoint: endpoint }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@@ -394,9 +401,10 @@ async function chunkedTranslate(text, lang, translateFn, chunkSize = 5000) {
|
||||
* Translates text using the selected translation provider
|
||||
* @param {string} text Text to translate
|
||||
* @param {string} lang Target language code
|
||||
* @param {string} provider Translation provider to use
|
||||
* @returns {Promise<string>} Translated text
|
||||
*/
|
||||
async function translate(text, lang) {
|
||||
async function translate(text, lang, provider = null) {
|
||||
try {
|
||||
if (text == '') {
|
||||
return '';
|
||||
@@ -406,13 +414,17 @@ async function translate(text, lang) {
|
||||
lang = extension_settings.translate.target_language;
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
provider = extension_settings.translate.provider;
|
||||
}
|
||||
|
||||
// split text by embedded images links
|
||||
const chunks = text.split(/!\[.*?]\([^)]*\)/);
|
||||
const links = [...text.matchAll(/!\[.*?]\([^)]*\)/g)];
|
||||
|
||||
let result = '';
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
result += await translateInner(chunks[i], lang);
|
||||
result += await translateInner(chunks[i], lang, provider);
|
||||
if (i < links.length) result += links[i][0];
|
||||
}
|
||||
|
||||
@@ -423,11 +435,21 @@ async function translate(text, lang) {
|
||||
}
|
||||
}
|
||||
|
||||
async function translateInner(text, lang) {
|
||||
/**
|
||||
* Common translation function that handles the translation logic
|
||||
* @param {string} text Text to translate
|
||||
* @param {string} lang Target language code
|
||||
* @param {string} provider Translation provider to use
|
||||
* @returns {Promise<string>} Translated text
|
||||
*/
|
||||
async function translateInner(text, lang, provider) {
|
||||
if (text == '') {
|
||||
return '';
|
||||
}
|
||||
switch (extension_settings.translate.provider) {
|
||||
if (!provider) {
|
||||
provider = extension_settings.translate.provider;
|
||||
}
|
||||
switch (provider) {
|
||||
case 'libre':
|
||||
return await translateProviderLibre(text, lang);
|
||||
case 'google':
|
||||
@@ -445,7 +467,7 @@ async function translateInner(text, lang) {
|
||||
case 'yandex':
|
||||
return await translateProviderYandex(text, lang);
|
||||
default:
|
||||
console.error('Unknown translation provider', extension_settings.translate.provider);
|
||||
console.error('Unknown translation provider', provider);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -600,18 +622,34 @@ jQuery(async () => {
|
||||
}
|
||||
|
||||
$('#translation_auto_mode').on('change', (event) => {
|
||||
if (!(event.target instanceof HTMLSelectElement)) {
|
||||
return;
|
||||
}
|
||||
extension_settings.translate.auto_mode = event.target.value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#translation_provider').on('change', (event) => {
|
||||
if (!(event.target instanceof HTMLSelectElement)) {
|
||||
return;
|
||||
}
|
||||
extension_settings.translate.provider = event.target.value;
|
||||
showKeysButton();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#translation_target_language').on('change', (event) => {
|
||||
if (!(event.target instanceof HTMLSelectElement)) {
|
||||
return;
|
||||
}
|
||||
extension_settings.translate.target_language = event.target.value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$('#deepl_api_endpoint').on('change', (event) => {
|
||||
if (!(event.target instanceof HTMLSelectElement)) {
|
||||
return;
|
||||
}
|
||||
extension_settings.translate.deepl_endpoint = event.target.value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
$(document).on('click', '.mes_translate', onMessageTranslateClick);
|
||||
$('#translate_key_button').on('click', async () => {
|
||||
const optionText = $('#translation_provider option:selected').text();
|
||||
@@ -687,6 +725,14 @@ jQuery(async () => {
|
||||
helpString: 'Translate text to a target language. If target language is not provided, the value from the extension settings will be used.',
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument('target', 'The target language code to translate to', ARGUMENT_TYPE.STRING, false, false, '', Object.values(languageCodes)),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'provider',
|
||||
description: 'The translation provider to use. If not provided, the value from the extension settings will be used.',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: false,
|
||||
acceptsMultiple: false,
|
||||
enumProvider: () => Array.from(document.getElementById('translation_provider').querySelectorAll('option')).map((option) => new SlashCommandEnumValue(option.value, option.text, enumTypes.name, enumIcons.server)),
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument('The text to translate', ARGUMENT_TYPE.STRING, true, false, ''),
|
||||
@@ -695,7 +741,8 @@ jQuery(async () => {
|
||||
const target = args?.target && Object.values(languageCodes).includes(String(args.target))
|
||||
? String(args.target)
|
||||
: extension_settings.translate.target_language;
|
||||
return await translate(String(value), target);
|
||||
const provider = args?.provider || extension_settings.translate.provider;
|
||||
return await translate(String(value), target, provider);
|
||||
},
|
||||
returns: ARGUMENT_TYPE.STRING,
|
||||
}));
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@
|
||||
</div>
|
||||
<div id="playback_rate_block" class="range-block">
|
||||
<hr>
|
||||
<div class="range-block-title justifyLeft" data-i18n="Audio Playback Speed">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<small data-i18n="Audio Playback Speed">Audio Playback Speed</small>
|
||||
</div>
|
||||
<div class="range-block-range-and-counter">
|
||||
@@ -80,4 +80,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -93,7 +93,7 @@
|
||||
.at-settings-separator {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 18x;
|
||||
padding: 18px;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #e1e1e1; /* Grey line */
|
||||
border-bottom: 1px solid #e1e1e1; /* Grey line */
|
||||
|
@@ -55,6 +55,19 @@ export function isFilterState(a, b) {
|
||||
return aKey === bKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fuzzy search categories
|
||||
* @type {{ characters: string, worldInfo: string, personas: string, tags: string, groups: string }}
|
||||
*/
|
||||
export const fuzzySearchCategories = Object.freeze({
|
||||
characters: 'characters',
|
||||
worldInfo: 'worldInfo',
|
||||
personas: 'personas',
|
||||
tags: 'tags',
|
||||
groups: 'groups',
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for filtering data.
|
||||
* @example
|
||||
@@ -72,6 +85,12 @@ export class FilterHelper {
|
||||
*/
|
||||
scoreCache;
|
||||
|
||||
/**
|
||||
* Cache for fuzzy search results per category.
|
||||
* @type {Object.<string, { resultMap: Map<string, any> }>}
|
||||
*/
|
||||
fuzzySearchCaches;
|
||||
|
||||
/**
|
||||
* Creates a new FilterHelper
|
||||
* @param {Function} onDataChanged Callback to trigger when the filter data changes
|
||||
@@ -79,6 +98,13 @@ export class FilterHelper {
|
||||
constructor(onDataChanged) {
|
||||
this.onDataChanged = onDataChanged;
|
||||
this.scoreCache = new Map();
|
||||
this.fuzzySearchCaches = {
|
||||
[fuzzySearchCategories.characters]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.worldInfo]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.personas]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.tags]: { resultMap: new Map() },
|
||||
[fuzzySearchCategories.groups]: { resultMap: new Map() },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +177,7 @@ export class FilterHelper {
|
||||
return data;
|
||||
}
|
||||
|
||||
const fuzzySearchResults = fuzzySearchWorldInfo(data, term);
|
||||
const fuzzySearchResults = fuzzySearchWorldInfo(data, term, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score])));
|
||||
|
||||
const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity));
|
||||
@@ -170,7 +196,7 @@ export class FilterHelper {
|
||||
return data;
|
||||
}
|
||||
|
||||
const fuzzySearchResults = fuzzySearchPersonas(data, term);
|
||||
const fuzzySearchResults = fuzzySearchPersonas(data, term, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score])));
|
||||
|
||||
const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name));
|
||||
@@ -289,9 +315,9 @@ export class FilterHelper {
|
||||
|
||||
// Save fuzzy search results and scores if enabled
|
||||
if (power_user.fuzzy_search) {
|
||||
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue);
|
||||
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue);
|
||||
const fuzzySearchTagsResult = fuzzySearchTags(searchValue);
|
||||
const fuzzySearchCharactersResults = fuzzySearchCharacters(searchValue, this.fuzzySearchCaches);
|
||||
const fuzzySearchGroupsResults = fuzzySearchGroups(searchValue, this.fuzzySearchCaches);
|
||||
const fuzzySearchTagsResult = fuzzySearchTags(searchValue, this.fuzzySearchCaches);
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchCharactersResults.map(i => [`character.${i.refIndex}`, i.score])));
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchGroupsResults.map(i => [`group.${i.item.id}`, i.score])));
|
||||
this.cacheScores(FILTER_TYPES.SEARCH, new Map(fuzzySearchTagsResult.map(i => [`tag.${i.item.id}`, i.score])));
|
||||
@@ -343,11 +369,14 @@ export class FilterHelper {
|
||||
* @param {object} options - Optional call parameters
|
||||
* @param {boolean} [options.clearScoreCache=true] - Whether the score cache should be cleared.
|
||||
* @param {Object.<FilterType, any>} [options.tempOverrides={}] - Temporarily override specific filters for this filter application
|
||||
* @param {boolean} [options.clearFuzzySearchCaches=true] - Whether the fuzzy search caches should be cleared.
|
||||
* @returns {any[]} The filtered data.
|
||||
*/
|
||||
applyFilters(data, { clearScoreCache = true, tempOverrides = {} } = {}) {
|
||||
applyFilters(data, { clearScoreCache = true, tempOverrides = {}, clearFuzzySearchCaches = true } = {}) {
|
||||
if (clearScoreCache) this.clearScoreCache();
|
||||
|
||||
if (clearFuzzySearchCaches) this.clearFuzzySearchCaches();
|
||||
|
||||
// Save original filter states
|
||||
const originalStates = {};
|
||||
for (const key in tempOverrides) {
|
||||
@@ -411,4 +440,13 @@ export class FilterHelper {
|
||||
this.scoreCache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears fuzzy search caches
|
||||
*/
|
||||
clearFuzzySearchCaches() {
|
||||
for (const cache of Object.values(this.fuzzySearchCaches)) {
|
||||
cache.resultMap.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -275,6 +275,20 @@ export function getGroupMembers(groupId = selected_group) {
|
||||
return group?.members.map(member => characters.find(x => x.avatar === member)) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the member names of a group. If the group is not selected, an empty array is returned.
|
||||
* @returns {string[]} An array of character names representing the members of the group.
|
||||
*/
|
||||
export function getGroupNames() {
|
||||
if (!selected_group) {
|
||||
return [];
|
||||
}
|
||||
const groupMembers = groups.find(x => x.id == selected_group)?.members;
|
||||
return Array.isArray(groupMembers)
|
||||
? groupMembers.map(x => characters.find(y => y.avatar === x)?.name).filter(x => x)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the character ID for a group member.
|
||||
* @param {string} arg 0-based member index or character name
|
||||
@@ -423,14 +437,20 @@ export function getGroupCharacterCards(groupId, characterId) {
|
||||
* @param {string} value Value to replace
|
||||
* @param {string} characterName Name of the character
|
||||
* @param {string} fieldName Name of the field
|
||||
* @param {function(string): string} [preprocess] Preprocess function
|
||||
* @returns {string} Prepared text
|
||||
* */
|
||||
function replaceAndPrepareForJoin(value, characterName, fieldName) {
|
||||
function replaceAndPrepareForJoin(value, characterName, fieldName, preprocess = null) {
|
||||
value = value.trim();
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Run preprocess function
|
||||
if (typeof preprocess === 'function') {
|
||||
value = preprocess(value);
|
||||
}
|
||||
|
||||
// Prepare and replace prefixes
|
||||
const prefix = customBaseChatReplace(group.generation_mode_join_prefix, fieldName, characterName);
|
||||
const suffix = customBaseChatReplace(group.generation_mode_join_suffix, fieldName, characterName);
|
||||
@@ -465,7 +485,7 @@ export function getGroupCharacterCards(groupId, characterId) {
|
||||
descriptions.push(replaceAndPrepareForJoin(character.description, character.name, 'Description'));
|
||||
personalities.push(replaceAndPrepareForJoin(character.personality, character.name, 'Personality'));
|
||||
scenarios.push(replaceAndPrepareForJoin(character.scenario, character.name, 'Scenario'));
|
||||
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages'));
|
||||
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages', (x) => !x.startsWith('<START>') ? `<START>\n${x}` : x));
|
||||
}
|
||||
|
||||
const description = descriptions.filter(x => x.length).join('\n');
|
||||
@@ -1265,18 +1285,20 @@ function getGroupCharacters({ doFilter, onlyMembers } = {}) {
|
||||
const thisGroup = openGroupId && groups.find((x) => x.id == openGroupId);
|
||||
let candidates = characters
|
||||
.filter((x) => isGroupMember(thisGroup, x.avatar) == onlyMembers)
|
||||
.map((x, index) => ({ item: x, id: index, type: 'character' }));
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
sortEntitiesList(candidates);
|
||||
}
|
||||
.map((x) => ({ item: x, id: characters.indexOf(x), type: 'character' }));
|
||||
|
||||
if (doFilter) {
|
||||
candidates = groupCandidatesFilter.applyFilters(candidates);
|
||||
}
|
||||
|
||||
if (onlyMembers) {
|
||||
candidates.sort(sortMembersFn);
|
||||
} else {
|
||||
const useFilterOrder = doFilter && !!$('#rm_group_filter').val();
|
||||
sortEntitiesList(candidates, useFilterOrder, groupCandidatesFilter);
|
||||
}
|
||||
|
||||
groupCandidatesFilter.clearFuzzySearchCaches();
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@@ -1863,32 +1885,38 @@ export async function deleteGroupChat(groupId, chatId) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function importGroupChat(formData) {
|
||||
await jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/chats/group/import',
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (data) {
|
||||
if (data.res) {
|
||||
const chatId = data.res;
|
||||
const group = groups.find(x => x.id == selected_group);
|
||||
|
||||
if (group) {
|
||||
group.chats.push(chatId);
|
||||
await editGroup(selected_group, true, true);
|
||||
await displayPastChats();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$('#create_button').removeAttr('disabled');
|
||||
},
|
||||
/**
|
||||
* Imports a group chat from a file and adds it to the group.
|
||||
* @param {FormData} formData Form data to send to the server
|
||||
* @param {EventTarget} eventTarget Element that triggered the import
|
||||
*/
|
||||
export async function importGroupChat(formData, eventTarget) {
|
||||
const headers = getRequestHeaders();
|
||||
delete headers['Content-Type'];
|
||||
const fetchResult = await fetch('/api/chats/group/import', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: formData,
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (fetchResult.ok) {
|
||||
const data = await fetchResult.json();
|
||||
if (data.res) {
|
||||
const chatId = data.res;
|
||||
const group = groups.find(x => x.id == selected_group);
|
||||
|
||||
if (group) {
|
||||
group.chats.push(chatId);
|
||||
await editGroup(selected_group, true, true);
|
||||
await displayPastChats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eventTarget instanceof HTMLInputElement) {
|
||||
eventTarget.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
|
||||
|
@@ -431,7 +431,8 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
|
||||
return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
|
||||
}
|
||||
|
||||
const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_behavior === names_behavior_types.FORCE);
|
||||
const includeNames = power_user.instruct.names_behavior === names_behavior_types.ALWAYS;
|
||||
const includeGroupNames = selected_group && [names_behavior_types.ALWAYS, names_behavior_types.FORCE].includes(power_user.instruct.names_behavior);
|
||||
|
||||
let inputPrefix = power_user.instruct.input_sequence || '';
|
||||
let outputPrefix = power_user.instruct.output_sequence || '';
|
||||
@@ -463,7 +464,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
|
||||
|
||||
for (const item of mesExamplesArray) {
|
||||
const cleanedItem = item.replace(/<START>/i, '{Example Dialogue:}').replace(/\r/gm, '');
|
||||
const blockExamples = parseExampleIntoIndividual(cleanedItem);
|
||||
const blockExamples = parseExampleIntoIndividual(cleanedItem, includeGroupNames);
|
||||
|
||||
if (blockExamples.length === 0) {
|
||||
continue;
|
||||
@@ -474,8 +475,9 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
|
||||
}
|
||||
|
||||
for (const example of blockExamples) {
|
||||
// If force group/persona names is set, we should override the include names for the user placeholder
|
||||
const includeThisName = includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user');
|
||||
// If group names were included, we don't want to add any additional prefix as it already was applied.
|
||||
// Otherwise, if force group/persona names is set, we should override the include names for the user placeholder
|
||||
const includeThisName = !includeGroupNames && (includeNames || (power_user.instruct.names_behavior === names_behavior_types.FORCE && example.name == 'example_user'));
|
||||
|
||||
const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix;
|
||||
const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix;
|
||||
@@ -489,7 +491,6 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
|
||||
if (formattedExamples.length === 0) {
|
||||
return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
|
||||
}
|
||||
|
||||
return formattedExamples;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { saveSettingsDebounced } from '../script.js';
|
||||
import { getTextTokens } from './tokenizers.js';
|
||||
import { uuidv4 } from './utils.js';
|
||||
import { getSortableDelay, uuidv4 } from './utils.js';
|
||||
|
||||
export const BIAS_CACHE = new Map();
|
||||
|
||||
@@ -16,7 +16,8 @@ export function displayLogitBias(logitBias, containerSelector) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(containerSelector).find('.logit_bias_list').empty();
|
||||
const list = $(containerSelector).find('.logit_bias_list');
|
||||
list.empty();
|
||||
|
||||
for (const entry of logitBias) {
|
||||
if (entry) {
|
||||
@@ -24,6 +25,27 @@ export function displayLogitBias(logitBias, containerSelector) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a sortable instance exists
|
||||
if (list.sortable('instance') !== undefined) {
|
||||
// Destroy the instance
|
||||
list.sortable('destroy');
|
||||
}
|
||||
|
||||
// Make the list sortable
|
||||
list.sortable({
|
||||
delay: getSortableDelay(),
|
||||
handle: '.drag-handle',
|
||||
stop: function () {
|
||||
const order = [];
|
||||
list.children().each(function () {
|
||||
order.unshift($(this).data('id'));
|
||||
});
|
||||
logitBias.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
|
||||
console.log('Logit bias reordered:', logitBias);
|
||||
saveSettingsDebounced();
|
||||
},
|
||||
});
|
||||
|
||||
BIAS_CACHE.delete(containerSelector);
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { Handlebars, moment, seedrandom, droll } from '../lib.js';
|
||||
import { chat, chat_metadata, main_api, getMaxContextSize, getCurrentChatId, substituteParams } from '../script.js';
|
||||
import { chat, chat_metadata, main_api, getMaxContextSize, getCurrentChatId, substituteParams, eventSource, event_types } from '../script.js';
|
||||
import { timestampToMoment, isDigitsOnly, getStringHash, escapeRegex, uuidv4 } from './utils.js';
|
||||
import { textgenerationwebui_banned_in_macros } from './textgen-settings.js';
|
||||
import { getInstructMacros } from './instruct-mode.js';
|
||||
import { getVariableMacros } from './variables.js';
|
||||
import { isMobile } from './RossAscends-mods.js';
|
||||
|
||||
/**
|
||||
* @typedef Macro
|
||||
@@ -516,8 +517,32 @@ export function evaluateMacros(content, env, postProcessFn) {
|
||||
break;
|
||||
}
|
||||
|
||||
content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args)));
|
||||
try {
|
||||
content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args)));
|
||||
} catch (e) {
|
||||
console.warn(`Macro content can't be replaced: ${macro.regex} in ${content}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
export function initMacros() {
|
||||
function initLastGenerationType() {
|
||||
let lastGenerationType = '';
|
||||
|
||||
MacrosParser.registerMacro('lastGenerationType', () => lastGenerationType);
|
||||
|
||||
eventSource.on(event_types.GENERATION_STARTED, (type, _params, isDryRun) => {
|
||||
if (isDryRun) return;
|
||||
lastGenerationType = type || 'normal';
|
||||
});
|
||||
|
||||
eventSource.on(event_types.CHAT_CHANGED, () => {
|
||||
lastGenerationType = '';
|
||||
});
|
||||
}
|
||||
|
||||
MacrosParser.registerMacro('isMobile', () => String(isMobile()));
|
||||
initLastGenerationType();
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ import {
|
||||
system_message_types,
|
||||
this_chid,
|
||||
} from '../script.js';
|
||||
import { selected_group } from './group-chats.js';
|
||||
import { getGroupNames, selected_group } from './group-chats.js';
|
||||
|
||||
import {
|
||||
chatCompletionDefaultPrompts,
|
||||
@@ -60,6 +60,7 @@ import {
|
||||
parseJsonFile,
|
||||
resetScrollHeight,
|
||||
stringFormat,
|
||||
uuidv4,
|
||||
} from './utils.js';
|
||||
import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js';
|
||||
import { isMobile } from './RossAscends-mods.js';
|
||||
@@ -99,19 +100,18 @@ const default_wi_format = '{0}';
|
||||
const default_new_chat_prompt = '[Start a new Chat]';
|
||||
const default_new_group_chat_prompt = '[Start a new group chat. Group members: {{group}}]';
|
||||
const default_new_example_chat_prompt = '[Example Chat]';
|
||||
const default_claude_human_sysprompt_message = 'Let\'s get started. Please generate your response based on the information and instructions provided above.';
|
||||
const default_continue_nudge_prompt = '[Continue the following message. Do not include ANY parts of the original message. Use capitalization and punctuation as if your reply is a part of the original message: {{lastChatMessage}}]';
|
||||
const default_bias = 'Default (none)';
|
||||
const default_personality_format = '[{{char}}\'s personality: {{personality}}]';
|
||||
const default_scenario_format = '[Circumstances and context of the dialogue: {{scenario}}]';
|
||||
const default_personality_format = '{{personality}}';
|
||||
const default_scenario_format = '{{scenario}}';
|
||||
const default_group_nudge_prompt = '[Write the next reply only as {{char}}.]';
|
||||
const default_bias_presets = {
|
||||
[default_bias]: [],
|
||||
'Anti-bond': [
|
||||
{ text: ' bond', value: -50 },
|
||||
{ text: ' future', value: -50 },
|
||||
{ text: ' bonding', value: -50 },
|
||||
{ text: ' connection', value: -25 },
|
||||
{ id: '22154f79-dd98-41bc-8e34-87015d6a0eaf', text: ' bond', value: -50 },
|
||||
{ id: '8ad2d5c4-d8ef-49e4-bc5e-13e7f4690e0f', text: ' future', value: -50 },
|
||||
{ id: '52a4b280-0956-4940-ac52-4111f83e4046', text: ' bonding', value: -50 },
|
||||
{ id: 'e63037c7-c9d1-4724-ab2d-7756008b433b', text: ' connection', value: -25 },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -183,6 +183,7 @@ export const chat_completion_sources = {
|
||||
ZEROONEAI: '01ai',
|
||||
BLOCKENTROPY: 'blockentropy',
|
||||
NANOGPT: 'nanogpt',
|
||||
DEEPSEEK: 'deepseek',
|
||||
};
|
||||
|
||||
const character_names_behavior = {
|
||||
@@ -204,9 +205,16 @@ const custom_prompt_post_processing_types = {
|
||||
/** @deprecated Use MERGE instead. */
|
||||
CLAUDE: 'claude',
|
||||
MERGE: 'merge',
|
||||
SEMI: 'semi',
|
||||
STRICT: 'strict',
|
||||
};
|
||||
|
||||
const openrouter_middleout_types = {
|
||||
AUTO: 'auto',
|
||||
ON: 'on',
|
||||
OFF: 'off',
|
||||
};
|
||||
|
||||
const sensitiveFields = [
|
||||
'reverse_proxy',
|
||||
'proxy_password',
|
||||
@@ -255,6 +263,7 @@ const default_settings = {
|
||||
nanogpt_model: 'gpt-4o-mini',
|
||||
zerooneai_model: 'yi-large',
|
||||
blockentropy_model: 'be-70b-base-llama3.1',
|
||||
deepseek_model: 'deepseek-chat',
|
||||
custom_model: '',
|
||||
custom_url: '',
|
||||
custom_include_body: '',
|
||||
@@ -267,6 +276,7 @@ const default_settings = {
|
||||
openrouter_sort_models: 'alphabetically',
|
||||
openrouter_providers: [],
|
||||
openrouter_allow_fallbacks: true,
|
||||
openrouter_middleout: openrouter_middleout_types.ON,
|
||||
jailbreak_system: false,
|
||||
reverse_proxy: '',
|
||||
chat_completion_source: chat_completion_sources.OPENAI,
|
||||
@@ -276,7 +286,6 @@ const default_settings = {
|
||||
proxy_password: '',
|
||||
assistant_prefill: '',
|
||||
assistant_impersonation: '',
|
||||
human_sysprompt_message: default_claude_human_sysprompt_message,
|
||||
claude_use_sysprompt: false,
|
||||
use_makersuite_sysprompt: true,
|
||||
use_alt_scale: false,
|
||||
@@ -289,6 +298,7 @@ const default_settings = {
|
||||
names_behavior: character_names_behavior.DEFAULT,
|
||||
continue_postfix: continue_postfix_types.SPACE,
|
||||
custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
|
||||
show_thoughts: false,
|
||||
seed: -1,
|
||||
n: 1,
|
||||
};
|
||||
@@ -332,6 +342,7 @@ const oai_settings = {
|
||||
nanogpt_model: 'gpt-4o-mini',
|
||||
zerooneai_model: 'yi-large',
|
||||
blockentropy_model: 'be-70b-base-llama3.1',
|
||||
deepseek_model: 'deepseek-chat',
|
||||
custom_model: '',
|
||||
custom_url: '',
|
||||
custom_include_body: '',
|
||||
@@ -344,6 +355,7 @@ const oai_settings = {
|
||||
openrouter_sort_models: 'alphabetically',
|
||||
openrouter_providers: [],
|
||||
openrouter_allow_fallbacks: true,
|
||||
openrouter_middleout: openrouter_middleout_types.ON,
|
||||
jailbreak_system: false,
|
||||
reverse_proxy: '',
|
||||
chat_completion_source: chat_completion_sources.OPENAI,
|
||||
@@ -353,7 +365,6 @@ const oai_settings = {
|
||||
proxy_password: '',
|
||||
assistant_prefill: '',
|
||||
assistant_impersonation: '',
|
||||
human_sysprompt_message: default_claude_human_sysprompt_message,
|
||||
claude_use_sysprompt: false,
|
||||
use_makersuite_sysprompt: true,
|
||||
use_alt_scale: false,
|
||||
@@ -366,6 +377,7 @@ const oai_settings = {
|
||||
names_behavior: character_names_behavior.DEFAULT,
|
||||
continue_postfix: continue_postfix_types.SPACE,
|
||||
custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
|
||||
show_thoughts: false,
|
||||
seed: -1,
|
||||
n: 1,
|
||||
};
|
||||
@@ -382,8 +394,8 @@ export let selected_proxy = proxies[0];
|
||||
let openai_setting_names;
|
||||
let openai_settings;
|
||||
|
||||
|
||||
let promptManager = null;
|
||||
/** @type {import('./PromptManager.js').PromptManager} */
|
||||
export let promptManager = null;
|
||||
|
||||
async function validateReverseProxy() {
|
||||
if (!oai_settings.reverse_proxy) {
|
||||
@@ -546,11 +558,15 @@ function setupChatCompletionPromptManager(openAiSettings) {
|
||||
* @returns {Message[]} Array of message objects
|
||||
*/
|
||||
export function parseExampleIntoIndividual(messageExampleString, appendNamesForGroup = true) {
|
||||
const groupBotNames = getGroupNames().map(name => `${name}:`);
|
||||
|
||||
let result = []; // array of msgs
|
||||
let tmp = messageExampleString.split('\n');
|
||||
let cur_msg_lines = [];
|
||||
let in_user = false;
|
||||
let in_bot = false;
|
||||
let botName = name2;
|
||||
|
||||
// DRY my cock and balls :)
|
||||
function add_msg(name, role, system_name) {
|
||||
// join different newlines (we split them by \n and join by \n)
|
||||
@@ -574,10 +590,14 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
|
||||
in_user = true;
|
||||
// we were in the bot mode previously, add the message
|
||||
if (in_bot) {
|
||||
add_msg(name2, 'system', 'example_assistant');
|
||||
add_msg(botName, 'system', 'example_assistant');
|
||||
}
|
||||
in_bot = false;
|
||||
} else if (cur_str.startsWith(name2 + ':')) {
|
||||
} else if (cur_str.startsWith(name2 + ':') || groupBotNames.some(n => cur_str.startsWith(n))) {
|
||||
if (!cur_str.startsWith(name2 + ':') && groupBotNames.length) {
|
||||
botName = cur_str.split(':')[0];
|
||||
}
|
||||
|
||||
in_bot = true;
|
||||
// we were in the user mode previously, add the message
|
||||
if (in_user) {
|
||||
@@ -592,7 +612,7 @@ export function parseExampleIntoIndividual(messageExampleString, appendNamesForG
|
||||
if (in_user) {
|
||||
add_msg(name1, 'system', 'example_user');
|
||||
} else if (in_bot) {
|
||||
add_msg(name2, 'system', 'example_assistant');
|
||||
add_msg(botName, 'system', 'example_assistant');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -614,8 +634,9 @@ function formatWorldInfo(value) {
|
||||
*
|
||||
* @param {Prompt[]} prompts - Array containing injection prompts.
|
||||
* @param {Object[]} messages - Array containing all messages.
|
||||
* @returns {Promise<Object[]>} - Array containing all messages with injections.
|
||||
*/
|
||||
function populationInjectionPrompts(prompts, messages) {
|
||||
async function populationInjectionPrompts(prompts, messages) {
|
||||
let totalInsertedMessages = 0;
|
||||
|
||||
const roleTypes = {
|
||||
@@ -638,7 +659,7 @@ function populationInjectionPrompts(prompts, messages) {
|
||||
// Get prompts for current role
|
||||
const rolePrompts = depthPrompts.filter(prompt => prompt.role === role).map(x => x.content).join(separator);
|
||||
// Get extension prompt
|
||||
const extensionPrompt = getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
|
||||
const extensionPrompt = await getExtensionPrompt(extension_prompt_types.IN_CHAT, i, separator, roleTypes[role], wrap);
|
||||
|
||||
const jointPrompt = [rolePrompts, extensionPrompt].filter(x => x).map(x => x.trim()).join(separator);
|
||||
|
||||
@@ -752,7 +773,9 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul
|
||||
if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) {
|
||||
// in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message
|
||||
if (chatPrompt.role === 'assistant') {
|
||||
const messageContent = [substituteParams(oai_settings.assistant_prefill), chatMessage.content].filter(x => x).join('\n\n');
|
||||
const supportsAssistantPrefill = oai_settings.chat_completion_source === chat_completion_sources.CLAUDE;
|
||||
const assistantPrefill = supportsAssistantPrefill ? substituteParams(oai_settings.assistant_prefill) : '';
|
||||
const messageContent = [assistantPrefill, chatMessage.content].filter(x => x).join('\n\n');
|
||||
const continueMessage = await Message.createAsync(chatMessage.role, messageContent, chatMessage.identifier);
|
||||
const collection = new MessageCollection('continuePrefill', continueMessage);
|
||||
chatCompletion.add(collection, -1);
|
||||
@@ -1023,7 +1046,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
|
||||
}
|
||||
|
||||
// Add in-chat injections
|
||||
messages = populationInjectionPrompts(absolutePrompts, messages);
|
||||
messages = await populationInjectionPrompts(absolutePrompts, messages);
|
||||
|
||||
// Decide whether dialogue examples should always be added
|
||||
if (power_user.pin_examples) {
|
||||
@@ -1054,9 +1077,9 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm
|
||||
* @param {string} options.systemPromptOverride
|
||||
* @param {string} options.jailbreakPromptOverride
|
||||
* @param {string} options.personaDescription
|
||||
* @returns {Object} prompts - The prepared and merged system and user-defined prompts.
|
||||
* @returns {Promise<Object>} prompts - The prepared and merged system and user-defined prompts.
|
||||
*/
|
||||
function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
|
||||
async function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts, systemPromptOverride, jailbreakPromptOverride, personaDescription }) {
|
||||
const scenarioText = Scenario && oai_settings.scenario_format ? substituteParams(oai_settings.scenario_format) : '';
|
||||
const charPersonalityText = charPersonality && oai_settings.personality_format ? substituteParams(oai_settings.personality_format) : '';
|
||||
const groupNudge = substituteParams(oai_settings.group_nudge_prompt);
|
||||
@@ -1145,6 +1168,9 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
|
||||
if (!extensionPrompts[key].value) continue;
|
||||
if (![extension_prompt_types.BEFORE_PROMPT, extension_prompt_types.IN_PROMPT].includes(prompt.position)) continue;
|
||||
|
||||
const hasFilter = typeof prompt.filter === 'function';
|
||||
if (hasFilter && !await prompt.filter()) continue;
|
||||
|
||||
systemPrompts.push({
|
||||
identifier: key.replace(/\W/g, '_'),
|
||||
position: getPromptPosition(prompt.position),
|
||||
@@ -1181,7 +1207,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
|
||||
|
||||
// Apply character-specific main prompt
|
||||
const systemPrompt = prompts.get('main') ?? null;
|
||||
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true) {
|
||||
const isSystemPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('main');
|
||||
if (systemPromptOverride && systemPrompt && systemPrompt.forbid_overrides !== true && !isSystemPromptDisabled) {
|
||||
const mainOriginalContent = systemPrompt.content;
|
||||
systemPrompt.content = systemPromptOverride;
|
||||
const mainReplacement = promptManager.preparePrompt(systemPrompt, mainOriginalContent);
|
||||
@@ -1190,7 +1217,8 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor
|
||||
|
||||
// Apply character-specific jailbreak
|
||||
const jailbreakPrompt = prompts.get('jailbreak') ?? null;
|
||||
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true) {
|
||||
const isJailbreakPromptDisabled = promptManager.isPromptDisabledForActiveCharacter('jailbreak');
|
||||
if (jailbreakPromptOverride && jailbreakPrompt && jailbreakPrompt.forbid_overrides !== true && !isJailbreakPromptDisabled) {
|
||||
const jbOriginalContent = jailbreakPrompt.content;
|
||||
jailbreakPrompt.content = jailbreakPromptOverride;
|
||||
const jbReplacement = promptManager.preparePrompt(jailbreakPrompt, jbOriginalContent);
|
||||
@@ -1255,7 +1283,7 @@ export async function prepareOpenAIMessages({
|
||||
|
||||
try {
|
||||
// Merge markers and ordered user prompts with system prompts
|
||||
const prompts = preparePromptsForChatCompletion({
|
||||
const prompts = await preparePromptsForChatCompletion({
|
||||
Scenario,
|
||||
charPersonality,
|
||||
name2,
|
||||
@@ -1501,6 +1529,8 @@ function getChatCompletionModel() {
|
||||
return oai_settings.blockentropy_model;
|
||||
case chat_completion_sources.NANOGPT:
|
||||
return oai_settings.nanogpt_model;
|
||||
case chat_completion_sources.DEEPSEEK:
|
||||
return oai_settings.deepseek_model;
|
||||
default:
|
||||
throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
|
||||
}
|
||||
@@ -1676,6 +1706,24 @@ function saveModelList(data) {
|
||||
|
||||
$('#model_nanogpt_select').val(oai_settings.nanogpt_model).trigger('change');
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
|
||||
$('#model_deepseek_select').empty();
|
||||
model_list.forEach((model) => {
|
||||
$('#model_deepseek_select').append(
|
||||
$('<option>', {
|
||||
value: model.id,
|
||||
text: model.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const selectedModel = model_list.find(model => model.id === oai_settings.deepseek_model);
|
||||
if (model_list.length > 0 && (!selectedModel || !oai_settings.deepseek_model)) {
|
||||
oai_settings.deepseek_model = model_list[0].id;
|
||||
}
|
||||
|
||||
$('#model_deepseek_select').val(oai_settings.deepseek_model).trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
|
||||
@@ -1815,6 +1863,8 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY;
|
||||
const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ;
|
||||
const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI;
|
||||
const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT;
|
||||
const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK;
|
||||
const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
|
||||
const isQuiet = type === 'quiet';
|
||||
const isImpersonate = type === 'impersonate';
|
||||
@@ -1862,6 +1912,8 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
'n': canMultiSwipe ? oai_settings.n : undefined,
|
||||
'user_name': name1,
|
||||
'char_name': name2,
|
||||
'group_names': getGroupNames(),
|
||||
'show_thoughts': Boolean(oai_settings.show_thoughts),
|
||||
};
|
||||
|
||||
// Empty array will produce a validation error
|
||||
@@ -1877,7 +1929,7 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
}
|
||||
|
||||
// Add logprobs request (currently OpenAI only, max 5 on their side)
|
||||
if (useLogprobs && (isOAI || isCustom)) {
|
||||
if (useLogprobs && (isOAI || isCustom || isDeepSeek)) {
|
||||
generate_data['logprobs'] = 5;
|
||||
}
|
||||
|
||||
@@ -1892,7 +1944,6 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
generate_data['top_k'] = Number(oai_settings.top_k_openai);
|
||||
generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt;
|
||||
generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
|
||||
generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message);
|
||||
// Don't add a prefill on quiet gens (summarization) and when using continue prefill.
|
||||
if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) {
|
||||
generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill);
|
||||
@@ -1907,6 +1958,7 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
generate_data['use_fallback'] = oai_settings.openrouter_use_fallback;
|
||||
generate_data['provider'] = oai_settings.openrouter_providers;
|
||||
generate_data['allow_fallbacks'] = oai_settings.openrouter_allow_fallbacks;
|
||||
generate_data['middleout'] = oai_settings.openrouter_middleout;
|
||||
|
||||
if (isTextCompletion) {
|
||||
generate_data['stop'] = getStoppingStrings(isImpersonate, isContinue);
|
||||
@@ -1975,7 +2027,12 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
delete generate_data.stop;
|
||||
}
|
||||
|
||||
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere) && oai_settings.seed >= 0) {
|
||||
// https://api-docs.deepseek.com/api/create-chat-completion
|
||||
if (isDeepSeek) {
|
||||
generate_data.top_p = generate_data.top_p || Number.EPSILON;
|
||||
}
|
||||
|
||||
if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) {
|
||||
generate_data['seed'] = oai_settings.seed;
|
||||
}
|
||||
|
||||
@@ -2076,7 +2133,7 @@ function getStreamingReply(data) {
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
|
||||
return data?.delta?.text || '';
|
||||
} else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
|
||||
return data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
||||
return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || '';
|
||||
} else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
|
||||
return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
|
||||
} else {
|
||||
@@ -2088,7 +2145,7 @@ function getStreamingReply(data) {
|
||||
* parseChatCompletionLogprobs converts the response data returned from a chat
|
||||
* completions-like source into an array of TokenLogprobs found in the response.
|
||||
* @param {Object} data - response data from a chat completions-like source
|
||||
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
*/
|
||||
function parseChatCompletionLogprobs(data) {
|
||||
if (!data) {
|
||||
@@ -2097,6 +2154,7 @@ function parseChatCompletionLogprobs(data) {
|
||||
|
||||
switch (oai_settings.chat_completion_source) {
|
||||
case chat_completion_sources.OPENAI:
|
||||
case chat_completion_sources.DEEPSEEK:
|
||||
case chat_completion_sources.CUSTOM:
|
||||
if (!data.choices?.length) {
|
||||
return null;
|
||||
@@ -2117,7 +2175,7 @@ function parseChatCompletionLogprobs(data) {
|
||||
* completion API and converts into the structure used by the Token Probabilities
|
||||
* view.
|
||||
* @param {{content: { token: string, logprob: number, top_logprobs: { token: string, logprob: number }[] }[]}} logprobs
|
||||
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
*/
|
||||
function parseOpenAIChatLogprobs(logprobs) {
|
||||
const { content } = logprobs ?? {};
|
||||
@@ -2145,7 +2203,7 @@ function parseOpenAIChatLogprobs(logprobs) {
|
||||
* completion API and converts into the structure used by the Token Probabilities
|
||||
* view.
|
||||
* @param {{tokens: string[], token_logprobs: number[], top_logprobs: { token: string, logprob: number }[][]}} logprobs
|
||||
* @returns {import('logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
* @returns {import('./logprobs.js').TokenLogprobs[] | null} converted logprobs
|
||||
*/
|
||||
function parseOpenAITextLogprobs(logprobs) {
|
||||
const { tokens, token_logprobs, top_logprobs } = logprobs ?? {};
|
||||
@@ -3009,12 +3067,14 @@ function loadOpenAISettings(data, settings) {
|
||||
oai_settings.openrouter_sort_models = settings.openrouter_sort_models ?? default_settings.openrouter_sort_models;
|
||||
oai_settings.openrouter_use_fallback = settings.openrouter_use_fallback ?? default_settings.openrouter_use_fallback;
|
||||
oai_settings.openrouter_allow_fallbacks = settings.openrouter_allow_fallbacks ?? default_settings.openrouter_allow_fallbacks;
|
||||
oai_settings.openrouter_middleout = settings.openrouter_middleout ?? default_settings.openrouter_middleout;
|
||||
oai_settings.ai21_model = settings.ai21_model ?? default_settings.ai21_model;
|
||||
oai_settings.mistralai_model = settings.mistralai_model ?? default_settings.mistralai_model;
|
||||
oai_settings.cohere_model = settings.cohere_model ?? default_settings.cohere_model;
|
||||
oai_settings.perplexity_model = settings.perplexity_model ?? default_settings.perplexity_model;
|
||||
oai_settings.groq_model = settings.groq_model ?? default_settings.groq_model;
|
||||
oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model;
|
||||
oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model;
|
||||
oai_settings.blockentropy_model = settings.blockentropy_model ?? default_settings.blockentropy_model;
|
||||
oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model;
|
||||
oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
|
||||
@@ -3030,10 +3090,10 @@ function loadOpenAISettings(data, settings) {
|
||||
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
|
||||
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
|
||||
oai_settings.assistant_impersonation = settings.assistant_impersonation ?? default_settings.assistant_impersonation;
|
||||
oai_settings.human_sysprompt_message = settings.human_sysprompt_message ?? default_settings.human_sysprompt_message;
|
||||
oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
|
||||
oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
|
||||
oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
|
||||
oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
|
||||
oai_settings.seed = settings.seed ?? default_settings.seed;
|
||||
oai_settings.n = settings.n ?? default_settings.n;
|
||||
|
||||
@@ -3070,7 +3130,6 @@ function loadOpenAISettings(data, settings) {
|
||||
$('#openai_proxy_password').val(oai_settings.proxy_password);
|
||||
$('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
|
||||
$('#claude_assistant_impersonation').val(oai_settings.assistant_impersonation);
|
||||
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
|
||||
$('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
|
||||
$('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
|
||||
|
||||
@@ -3097,6 +3156,8 @@ function loadOpenAISettings(data, settings) {
|
||||
$(`#model_groq_select option[value="${oai_settings.groq_model}"`).attr('selected', true);
|
||||
$('#model_nanogpt_select').val(oai_settings.nanogpt_model);
|
||||
$(`#model_nanogpt_select option[value="${oai_settings.nanogpt_model}"`).attr('selected', true);
|
||||
$('#model_deepseek_select').val(oai_settings.deepseek_model);
|
||||
$(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true);
|
||||
$('#model_01ai_select').val(oai_settings.zerooneai_model);
|
||||
$('#model_blockentropy_select').val(oai_settings.blockentropy_model);
|
||||
$('#custom_model_id').val(oai_settings.custom_model);
|
||||
@@ -3119,6 +3180,7 @@ function loadOpenAISettings(data, settings) {
|
||||
$('#openrouter_group_models').prop('checked', oai_settings.openrouter_group_models);
|
||||
$('#openrouter_allow_fallbacks').prop('checked', oai_settings.openrouter_allow_fallbacks);
|
||||
$('#openrouter_providers_chat').val(oai_settings.openrouter_providers).trigger('change');
|
||||
$('#openrouter_middleout').val(oai_settings.openrouter_middleout);
|
||||
$('#squash_system_messages').prop('checked', oai_settings.squash_system_messages);
|
||||
$('#continue_prefill').prop('checked', oai_settings.continue_prefill);
|
||||
$('#openai_function_calling').prop('checked', oai_settings.function_calling);
|
||||
@@ -3159,6 +3221,7 @@ function loadOpenAISettings(data, settings) {
|
||||
$('#repetition_penalty_counter_openai').val(Number(oai_settings.repetition_penalty_openai));
|
||||
$('#seed_openai').val(oai_settings.seed);
|
||||
$('#n_openai').val(oai_settings.n);
|
||||
$('#openai_show_thoughts').prop('checked', oai_settings.show_thoughts);
|
||||
|
||||
if (settings.reverse_proxy !== undefined) oai_settings.reverse_proxy = settings.reverse_proxy;
|
||||
$('#openai_reverse_proxy').val(oai_settings.reverse_proxy);
|
||||
@@ -3167,6 +3230,14 @@ function loadOpenAISettings(data, settings) {
|
||||
|
||||
$('#openai_logit_bias_preset').empty();
|
||||
for (const preset of Object.keys(oai_settings.bias_presets)) {
|
||||
// Backfill missing IDs
|
||||
if (Array.isArray(oai_settings.bias_presets[preset])) {
|
||||
oai_settings.bias_presets[preset].forEach((bias) => {
|
||||
if (bias && !bias.id) {
|
||||
bias.id = uuidv4();
|
||||
}
|
||||
});
|
||||
}
|
||||
const option = document.createElement('option');
|
||||
option.innerText = preset;
|
||||
option.value = preset;
|
||||
@@ -3275,7 +3346,7 @@ async function getStatusOpen() {
|
||||
chat_completion_source: oai_settings.chat_completion_source,
|
||||
};
|
||||
|
||||
if (oai_settings.reverse_proxy && (oai_settings.chat_completion_source === chat_completion_sources.OPENAI || oai_settings.chat_completion_source === chat_completion_sources.CLAUDE)) {
|
||||
if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {
|
||||
await validateReverseProxy();
|
||||
}
|
||||
|
||||
@@ -3351,6 +3422,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
||||
openrouter_sort_models: settings.openrouter_sort_models,
|
||||
openrouter_providers: settings.openrouter_providers,
|
||||
openrouter_allow_fallbacks: settings.openrouter_allow_fallbacks,
|
||||
openrouter_middleout: settings.openrouter_middleout,
|
||||
ai21_model: settings.ai21_model,
|
||||
mistralai_model: settings.mistralai_model,
|
||||
cohere_model: settings.cohere_model,
|
||||
@@ -3400,7 +3472,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
||||
show_external_models: settings.show_external_models,
|
||||
assistant_prefill: settings.assistant_prefill,
|
||||
assistant_impersonation: settings.assistant_impersonation,
|
||||
human_sysprompt_message: settings.human_sysprompt_message,
|
||||
claude_use_sysprompt: settings.claude_use_sysprompt,
|
||||
use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
|
||||
use_alt_scale: settings.use_alt_scale,
|
||||
@@ -3411,6 +3482,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
|
||||
continue_prefill: settings.continue_prefill,
|
||||
continue_postfix: settings.continue_postfix,
|
||||
function_calling: settings.function_calling,
|
||||
show_thoughts: settings.show_thoughts,
|
||||
seed: settings.seed,
|
||||
n: settings.n,
|
||||
};
|
||||
@@ -3456,7 +3528,8 @@ function onLogitBiasPresetChange() {
|
||||
}
|
||||
|
||||
oai_settings.bias_preset_selected = value;
|
||||
$('.openai_logit_bias_list').empty();
|
||||
const list = $('.openai_logit_bias_list');
|
||||
list.empty();
|
||||
|
||||
for (const entry of preset) {
|
||||
if (entry) {
|
||||
@@ -3464,12 +3537,33 @@ function onLogitBiasPresetChange() {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a sortable instance exists
|
||||
if (list.sortable('instance') !== undefined) {
|
||||
// Destroy the instance
|
||||
list.sortable('destroy');
|
||||
}
|
||||
|
||||
// Make the list sortable
|
||||
list.sortable({
|
||||
delay: getSortableDelay(),
|
||||
handle: '.drag-handle',
|
||||
stop: function () {
|
||||
const order = [];
|
||||
list.children().each(function () {
|
||||
order.unshift($(this).data('id'));
|
||||
});
|
||||
preset.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
|
||||
console.log('Logit bias reordered:', preset);
|
||||
saveSettingsDebounced();
|
||||
},
|
||||
});
|
||||
|
||||
biasCache = undefined;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function createNewLogitBiasEntry() {
|
||||
const entry = { text: '', value: 0 };
|
||||
const entry = { id: uuidv4(), text: '', value: 0 };
|
||||
oai_settings.bias_presets[oai_settings.bias_preset_selected].push(entry);
|
||||
biasCache = undefined;
|
||||
createLogitBiasListItem(entry);
|
||||
@@ -3477,11 +3571,14 @@ function createNewLogitBiasEntry() {
|
||||
}
|
||||
|
||||
function createLogitBiasListItem(entry) {
|
||||
const id = oai_settings.bias_presets[oai_settings.bias_preset_selected].indexOf(entry);
|
||||
if (!entry.id) {
|
||||
entry.id = uuidv4();
|
||||
}
|
||||
const id = entry.id;
|
||||
const template = $('#openai_logit_bias_template .openai_logit_bias_form').clone();
|
||||
template.data('id', id);
|
||||
template.find('.openai_logit_bias_text').val(entry.text).on('input', function () {
|
||||
oai_settings.bias_presets[oai_settings.bias_preset_selected][id].text = String($(this).val());
|
||||
entry.text = String($(this).val());
|
||||
biasCache = undefined;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
@@ -3500,13 +3597,17 @@ function createLogitBiasListItem(entry) {
|
||||
value = max;
|
||||
}
|
||||
|
||||
oai_settings.bias_presets[oai_settings.bias_preset_selected][id].value = value;
|
||||
entry.value = value;
|
||||
biasCache = undefined;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
template.find('.openai_logit_bias_remove').on('click', function () {
|
||||
$(this).closest('.openai_logit_bias_form').remove();
|
||||
oai_settings.bias_presets[oai_settings.bias_preset_selected].splice(id, 1);
|
||||
const preset = oai_settings.bias_presets[oai_settings.bias_preset_selected];
|
||||
const index = preset.findIndex(item => item.id === id);
|
||||
if (index >= 0) {
|
||||
preset.splice(index, 1);
|
||||
}
|
||||
onLogitBiasPresetChange();
|
||||
});
|
||||
$('.openai_logit_bias_list').prepend(template);
|
||||
@@ -3686,6 +3787,9 @@ async function onLogitBiasPresetImportFileChange(e) {
|
||||
if (typeof entry == 'object' && entry !== null) {
|
||||
if (Object.hasOwn(entry, 'text') &&
|
||||
Object.hasOwn(entry, 'value')) {
|
||||
if (!entry.id) {
|
||||
entry.id = uuidv4();
|
||||
}
|
||||
validEntries.push(entry);
|
||||
}
|
||||
}
|
||||
@@ -3786,12 +3890,14 @@ function onSettingsPresetChange() {
|
||||
openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false],
|
||||
openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false],
|
||||
openrouter_allow_fallbacks: ['#openrouter_allow_fallbacks', 'openrouter_allow_fallbacks', true],
|
||||
openrouter_middleout: ['#openrouter_middleout', 'openrouter_middleout', false],
|
||||
ai21_model: ['#model_ai21_select', 'ai21_model', false],
|
||||
mistralai_model: ['#model_mistralai_select', 'mistralai_model', false],
|
||||
cohere_model: ['#model_cohere_select', 'cohere_model', false],
|
||||
perplexity_model: ['#model_perplexity_select', 'perplexity_model', false],
|
||||
groq_model: ['#model_groq_select', 'groq_model', false],
|
||||
nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false],
|
||||
deepseek_model: ['#model_deepseek_select', 'deepseek_model', false],
|
||||
zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false],
|
||||
blockentropy_model: ['#model_blockentropy_select', 'blockentropy_model', false],
|
||||
custom_model: ['#custom_model_id', 'custom_model', false],
|
||||
@@ -3825,7 +3931,6 @@ function onSettingsPresetChange() {
|
||||
proxy_password: ['#openai_proxy_password', 'proxy_password', false],
|
||||
assistant_prefill: ['#claude_assistant_prefill', 'assistant_prefill', false],
|
||||
assistant_impersonation: ['#claude_assistant_impersonation', 'assistant_impersonation', false],
|
||||
human_sysprompt_message: ['#claude_human_sysprompt_textarea', 'human_sysprompt_message', false],
|
||||
claude_use_sysprompt: ['#claude_use_sysprompt', 'claude_use_sysprompt', true],
|
||||
use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true],
|
||||
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
|
||||
@@ -3835,6 +3940,7 @@ function onSettingsPresetChange() {
|
||||
continue_prefill: ['#continue_prefill', 'continue_prefill', true],
|
||||
continue_postfix: ['#continue_postfix', 'continue_postfix', false],
|
||||
function_calling: ['#openai_function_calling', 'function_calling', true],
|
||||
show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true],
|
||||
seed: ['#seed_openai', 'seed', false],
|
||||
n: ['#n_openai', 'n', false],
|
||||
};
|
||||
@@ -4051,6 +4157,16 @@ async function onModelChange() {
|
||||
oai_settings.nanogpt_model = value;
|
||||
}
|
||||
|
||||
if ($(this).is('#model_deepseek_select')) {
|
||||
if (!value) {
|
||||
console.debug('Null DeepSeek model selected. Ignoring.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('DeepSeek model changed to', value);
|
||||
oai_settings.deepseek_model = value;
|
||||
}
|
||||
|
||||
if (value && $(this).is('#model_01ai_select')) {
|
||||
console.log('01.AI model changed to', value);
|
||||
oai_settings.zerooneai_model = value;
|
||||
@@ -4082,19 +4198,14 @@ async function onModelChange() {
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
|
||||
if (oai_settings.max_context_unlocked) {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121')) {
|
||||
} else if (value.includes('gemini-exp-1114') || value.includes('gemini-exp-1121') || value.includes('gemini-2.0-flash-thinking-exp-1219')) {
|
||||
$('#openai_max_context').attr('max', max_32k);
|
||||
} else if (value.includes('gemini-1.5-pro')) {
|
||||
} else if (value.includes('gemini-1.5-pro') || value.includes('gemini-exp-1206')) {
|
||||
$('#openai_max_context').attr('max', max_2mil);
|
||||
} else if (value.includes('gemini-1.5-flash')) {
|
||||
} else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) {
|
||||
$('#openai_max_context').attr('max', max_1mil);
|
||||
} else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') {
|
||||
$('#openai_max_context').attr('max', max_16k);
|
||||
} else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') {
|
||||
$('#openai_max_context').attr('max', max_32k);
|
||||
} else if (value === 'text-bison-001') {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
// The ultra endpoints are possibly dead:
|
||||
} else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') {
|
||||
$('#openai_max_context').attr('max', max_32k);
|
||||
} else {
|
||||
@@ -4216,16 +4327,13 @@ async function onModelChange() {
|
||||
if (oai_settings.max_context_unlocked) {
|
||||
$('#openai_max_context').attr('max', unlocked_max);
|
||||
}
|
||||
else if (['command-light', 'command'].includes(oai_settings.cohere_model)) {
|
||||
else if (['command-light-nightly', 'command-light', 'command'].includes(oai_settings.cohere_model)) {
|
||||
$('#openai_max_context').attr('max', max_4k);
|
||||
}
|
||||
else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
}
|
||||
else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-expanse-32b'].includes(oai_settings.cohere_model)) {
|
||||
else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-23', 'c4ai-aya-expanse-32b', 'command-nightly'].includes(oai_settings.cohere_model)) {
|
||||
$('#openai_max_context').attr('max', max_128k);
|
||||
}
|
||||
else if (['c4ai-aya-23', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) {
|
||||
else if (['c4ai-aya-23-8b', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
}
|
||||
else {
|
||||
@@ -4276,7 +4384,7 @@ async function onModelChange() {
|
||||
else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) {
|
||||
$('#openai_max_context').attr('max', max_8k);
|
||||
}
|
||||
else if (oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
|
||||
else if (oai_settings.groq_model.includes('llama-3.3') || oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) {
|
||||
$('#openai_max_context').attr('max', max_128k);
|
||||
}
|
||||
else if (oai_settings.groq_model.includes('llama3-groq')) {
|
||||
@@ -4375,6 +4483,22 @@ async function onModelChange() {
|
||||
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
|
||||
if (oai_settings.max_context_unlocked) {
|
||||
$('#openai_max_context').attr('max', unlocked_max);
|
||||
} else if (oai_settings.deepseek_model == 'deepseek-chat') {
|
||||
$('#openai_max_context').attr('max', max_64k);
|
||||
} else if (oai_settings.deepseek_model == 'deepseek-coder') {
|
||||
$('#openai_max_context').attr('max', max_16k);
|
||||
} else {
|
||||
$('#openai_max_context').attr('max', max_64k);
|
||||
}
|
||||
|
||||
oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
|
||||
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
|
||||
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
|
||||
oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1);
|
||||
$('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input');
|
||||
@@ -4592,6 +4716,19 @@ async function onConnectButtonClick(e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
|
||||
const api_key_deepseek = String($('#api_key_deepseek').val()).trim();
|
||||
|
||||
if (api_key_deepseek.length) {
|
||||
await writeSecret(SECRET_KEYS.DEEPSEEK, api_key_deepseek);
|
||||
}
|
||||
|
||||
if (!secret_state[SECRET_KEYS.DEEPSEEK]) {
|
||||
console.log('No secret key saved for DeepSeek');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) {
|
||||
const api_key_01ai = String($('#api_key_01ai').val()).trim();
|
||||
|
||||
@@ -4673,14 +4810,13 @@ function toggleChatCompletionForms() {
|
||||
else if (oai_settings.chat_completion_source == chat_completion_sources.BLOCKENTROPY) {
|
||||
$('#model_blockentropy_select').trigger('change');
|
||||
}
|
||||
else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) {
|
||||
$('#model_deepseek_select').trigger('change');
|
||||
}
|
||||
$('[data-source]').each(function () {
|
||||
const validSources = $(this).data('source').split(',');
|
||||
$(this).toggle(validSources.includes(oai_settings.chat_completion_source));
|
||||
});
|
||||
|
||||
if (chat_completion_sources.CLAUDE == oai_settings.chat_completion_source) {
|
||||
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
|
||||
}
|
||||
}
|
||||
|
||||
async function testApiConnection() {
|
||||
@@ -4762,6 +4898,8 @@ export function isImageInliningSupported() {
|
||||
// gultra just isn't being offered as multimodal, thanks google.
|
||||
const visionSupportedModels = [
|
||||
'gpt-4-vision',
|
||||
'gemini-2.0-flash-thinking-exp-1219',
|
||||
'gemini-2.0-flash-exp',
|
||||
'gemini-1.5-flash',
|
||||
'gemini-1.5-flash-latest',
|
||||
'gemini-1.5-flash-001',
|
||||
@@ -4772,6 +4910,7 @@ export function isImageInliningSupported() {
|
||||
'gemini-1.5-flash-8b-exp-0924',
|
||||
'gemini-exp-1114',
|
||||
'gemini-exp-1121',
|
||||
'gemini-exp-1206',
|
||||
'gemini-1.0-pro-vision-latest',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-pro-latest',
|
||||
@@ -4779,7 +4918,6 @@ export function isImageInliningSupported() {
|
||||
'gemini-1.5-pro-002',
|
||||
'gemini-1.5-pro-exp-0801',
|
||||
'gemini-1.5-pro-exp-0827',
|
||||
'gemini-pro-vision',
|
||||
'claude-3',
|
||||
'claude-3-5',
|
||||
'gpt-4-turbo',
|
||||
@@ -5036,7 +5174,6 @@ export function initOpenAI() {
|
||||
|
||||
$('#claude_use_sysprompt').on('change', function () {
|
||||
oai_settings.claude_use_sysprompt = !!$('#claude_use_sysprompt').prop('checked');
|
||||
$('#claude_human_sysprompt_message_block').toggle(oai_settings.claude_use_sysprompt);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
@@ -5113,12 +5250,6 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#claude_human_sysprompt_message_restore').on('click', function () {
|
||||
oai_settings.human_sysprompt_message = default_claude_human_sysprompt_message;
|
||||
$('#claude_human_sysprompt_textarea').val(oai_settings.human_sysprompt_message);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#newgroupchat_prompt_restore').on('click', function () {
|
||||
oai_settings.new_group_chat_prompt = default_new_group_chat_prompt;
|
||||
$('#newgroupchat_prompt_textarea').val(oai_settings.new_group_chat_prompt);
|
||||
@@ -5210,11 +5341,6 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#claude_human_sysprompt_textarea').on('input', function () {
|
||||
oai_settings.human_sysprompt_message = String($('#claude_human_sysprompt_textarea').val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#openrouter_use_fallback').on('input', function () {
|
||||
oai_settings.openrouter_use_fallback = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
@@ -5235,6 +5361,11 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#openrouter_middleout').on('input', function () {
|
||||
oai_settings.openrouter_middleout = String($(this).val());
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#squash_system_messages').on('input', function () {
|
||||
oai_settings.squash_system_messages = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
@@ -5345,6 +5476,11 @@ export function initOpenAI() {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#openai_show_thoughts').on('input', function () {
|
||||
oai_settings.show_thoughts = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
if (!CSS.supports('field-sizing', 'content')) {
|
||||
$(document).on('input', '#openai_settings .autoSetHeight', function () {
|
||||
resetScrollHeight($(this));
|
||||
@@ -5390,6 +5526,7 @@ export function initOpenAI() {
|
||||
$('#model_perplexity_select').on('change', onModelChange);
|
||||
$('#model_groq_select').on('change', onModelChange);
|
||||
$('#model_nanogpt_select').on('change', onModelChange);
|
||||
$('#model_deepseek_select').on('change', onModelChange);
|
||||
$('#model_01ai_select').on('change', onModelChange);
|
||||
$('#model_blockentropy_select').on('change', onModelChange);
|
||||
$('#model_custom_select').on('change', onModelChange);
|
||||
|
@@ -21,8 +21,10 @@ import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSuppor
|
||||
import { debounce_timeout } from './constants.js';
|
||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||
import { selected_group } from './group-chats.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup } from './popup.js';
|
||||
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
|
||||
import { t } from './i18n.js';
|
||||
import { openWorldInfoEditor, world_names } from './world-info.js';
|
||||
import { renderTemplateAsync } from './templates.js';
|
||||
|
||||
let savePersonasPage = 0;
|
||||
const GRID_STORAGE_KEY = 'Personas_GridView';
|
||||
@@ -375,6 +377,7 @@ export function initPersona(avatarId, personaName, personaDescription) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
|
||||
saveSettingsDebounced();
|
||||
@@ -418,6 +421,7 @@ export async function convertCharacterToPersona(characterId = null) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
|
||||
// If the user is currently using this persona, update the description
|
||||
@@ -461,6 +465,7 @@ export function setPersonaDescription() {
|
||||
.val(power_user.persona_description_role)
|
||||
.find(`option[value="${power_user.persona_description_role}"]`)
|
||||
.prop('selected', String(true));
|
||||
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
|
||||
countPersonaDescriptionTokens();
|
||||
}
|
||||
|
||||
@@ -490,6 +495,7 @@ async function updatePersonaNameIfExists(avatarId, newName) {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
console.log(`Created persona name for ${avatarId} as ${newName}`);
|
||||
}
|
||||
@@ -535,6 +541,7 @@ async function bindUserNameToPersona(e) {
|
||||
position: isCurrentPersona ? power_user.persona_description_position : persona_description_positions.IN_PROMPT,
|
||||
depth: isCurrentPersona ? power_user.persona_description_depth : DEFAULT_DEPTH,
|
||||
role: isCurrentPersona ? power_user.persona_description_role : DEFAULT_ROLE,
|
||||
lorebook: isCurrentPersona ? power_user.persona_description_lorebook : '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -579,12 +586,20 @@ function selectCurrentPersona() {
|
||||
power_user.persona_description_position = descriptor.position ?? persona_description_positions.IN_PROMPT;
|
||||
power_user.persona_description_depth = descriptor.depth ?? DEFAULT_DEPTH;
|
||||
power_user.persona_description_role = descriptor.role ?? DEFAULT_ROLE;
|
||||
power_user.persona_description_lorebook = descriptor.lorebook ?? '';
|
||||
} else {
|
||||
power_user.persona_description = '';
|
||||
power_user.persona_description_position = persona_description_positions.IN_PROMPT;
|
||||
power_user.persona_description_depth = DEFAULT_DEPTH;
|
||||
power_user.persona_description_role = DEFAULT_ROLE;
|
||||
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.IN_PROMPT, depth: DEFAULT_DEPTH, role: DEFAULT_ROLE };
|
||||
power_user.persona_description_lorebook = '';
|
||||
power_user.persona_descriptions[user_avatar] = {
|
||||
description: '',
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
}
|
||||
|
||||
setPersonaDescription();
|
||||
@@ -652,6 +667,7 @@ async function lockPersona() {
|
||||
position: persona_description_positions.IN_PROMPT,
|
||||
depth: DEFAULT_DEPTH,
|
||||
role: DEFAULT_ROLE,
|
||||
lorebook: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -731,6 +747,7 @@ function onPersonaDescriptionInput() {
|
||||
position: Number($('#persona_description_position').find(':selected').val()),
|
||||
depth: Number($('#persona_depth_value').val()),
|
||||
role: Number($('#persona_depth_role').find(':selected').val()),
|
||||
lorebook: '',
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
@@ -766,6 +783,52 @@ function onPersonaDescriptionDepthRoleInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a popup to set the lorebook for the current persona.
|
||||
* @param {PointerEvent} event Click event
|
||||
*/
|
||||
async function onPersonaLoreButtonClick(event) {
|
||||
const personaName = power_user.personas[user_avatar];
|
||||
const selectedLorebook = power_user.persona_description_lorebook;
|
||||
|
||||
if (!personaName) {
|
||||
toastr.warning(t`You must bind a name to this persona before you can set a lorebook.`, t`Persona name not set`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.altKey && selectedLorebook) {
|
||||
openWorldInfoEditor(selectedLorebook);
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $(await renderTemplateAsync('personaLorebook'));
|
||||
|
||||
const worldSelect = template.find('select');
|
||||
template.find('.persona_name').text(personaName);
|
||||
|
||||
for (const worldName of world_names) {
|
||||
const option = document.createElement('option');
|
||||
option.value = worldName;
|
||||
option.innerText = worldName;
|
||||
option.selected = selectedLorebook === worldName;
|
||||
worldSelect.append(option);
|
||||
}
|
||||
|
||||
worldSelect.on('change', function () {
|
||||
power_user.persona_description_lorebook = String($(this).val());
|
||||
|
||||
if (power_user.personas[user_avatar]) {
|
||||
const object = getOrCreatePersonaDescriptor();
|
||||
object.lorebook = power_user.persona_description_lorebook;
|
||||
}
|
||||
|
||||
$('#persona_lore_button').toggleClass('world_set', !!power_user.persona_description_lorebook);
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
await callGenericPopup(template, POPUP_TYPE.TEXT);
|
||||
}
|
||||
|
||||
function onPersonaDescriptionPositionInput() {
|
||||
power_user.persona_description_position = Number(
|
||||
$('#persona_description_position').find(':selected').val(),
|
||||
@@ -789,6 +852,7 @@ function getOrCreatePersonaDescriptor() {
|
||||
position: power_user.persona_description_position,
|
||||
depth: power_user.persona_description_depth,
|
||||
role: power_user.persona_description_role,
|
||||
lorebook: power_user.persona_description_lorebook,
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
@@ -1038,6 +1102,7 @@ async function duplicatePersona(avatarId) {
|
||||
position: descriptor?.position ?? persona_description_positions.IN_PROMPT,
|
||||
depth: descriptor?.depth ?? DEFAULT_DEPTH,
|
||||
role: descriptor?.role ?? DEFAULT_ROLE,
|
||||
lorebook: descriptor?.lorebook ?? '',
|
||||
};
|
||||
|
||||
await uploadUserAvatar(getUserAvatar(avatarId), newAvatarId);
|
||||
@@ -1055,6 +1120,7 @@ export function initPersonas() {
|
||||
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
|
||||
$('#persona_depth_value').on('input', onPersonaDescriptionDepthValueInput);
|
||||
$('#persona_depth_role').on('input', onPersonaDescriptionDepthRoleInput);
|
||||
$('#persona_lore_button').on('click', onPersonaLoreButtonClick);
|
||||
$('#personas_backup').on('click', onBackupPersonas);
|
||||
$('#personas_restore').on('click', () => $('#personas_restore_input').trigger('click'));
|
||||
$('#personas_restore_input').on('change', onPersonasRestoreInput);
|
||||
|
@@ -75,7 +75,7 @@ const showPopupHelper = {
|
||||
* Asynchronously displays an input popup with the given header and text, and returns the user's input.
|
||||
*
|
||||
* @param {string?} header - The header text for the popup.
|
||||
* @param {string?} text - The main text for the popup.
|
||||
* @param {string?} [text] - The main text for the popup.
|
||||
* @param {string} [defaultValue=''] - The default value for the input field.
|
||||
* @param {PopupOptions} [popupOptions={}] - Options for the popup.
|
||||
* @return {Promise<string?>} A Promise that resolves with the user's input.
|
||||
@@ -94,7 +94,7 @@ const showPopupHelper = {
|
||||
* Asynchronously displays a confirmation popup with the given header and text, returning the clicked result button value.
|
||||
*
|
||||
* @param {string?} header - The header text for the popup.
|
||||
* @param {string?} text - The main text for the popup.
|
||||
* @param {string?} [text] - The main text for the popup.
|
||||
* @param {PopupOptions} [popupOptions={}] - Options for the popup.
|
||||
* @return {Promise<POPUP_RESULT?>} A Promise that resolves with the result of the user's interaction.
|
||||
*/
|
||||
|
@@ -53,13 +53,13 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE
|
||||
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { POPUP_TYPE, callGenericPopup } from './popup.js';
|
||||
import { loadSystemPrompts } from './sysprompt.js';
|
||||
import { fuzzySearchCategories } from './filters.js';
|
||||
|
||||
export {
|
||||
loadPowerUserSettings,
|
||||
loadMovingUIState,
|
||||
collapseNewlines,
|
||||
playMessageSound,
|
||||
sortEntitiesList,
|
||||
fixMarkdown,
|
||||
power_user,
|
||||
send_on_enter_options,
|
||||
@@ -245,6 +245,7 @@ let power_user = {
|
||||
},
|
||||
|
||||
context_derived: false,
|
||||
context_size_derived: false,
|
||||
|
||||
sysprompt: {
|
||||
enabled: true,
|
||||
@@ -260,6 +261,7 @@ let power_user = {
|
||||
persona_description_position: persona_description_positions.IN_PROMPT,
|
||||
persona_description_role: 0,
|
||||
persona_description_depth: 2,
|
||||
persona_description_lorebook: '',
|
||||
persona_show_notifications: true,
|
||||
persona_sort_order: 'asc',
|
||||
|
||||
@@ -471,9 +473,9 @@ function switchCompactInputArea() {
|
||||
$('#compact_input_area').prop('checked', power_user.compact_input_area);
|
||||
}
|
||||
|
||||
export function switchSwipeNumAllMessages() {
|
||||
function switchSwipeNumAllMessages() {
|
||||
$('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages);
|
||||
$('.mes:not(.last_mes) .swipes-counter').css('opacity', '').toggle(power_user.show_swipe_num_all_messages);
|
||||
$('body').toggleClass('swipeAllMessages', !!power_user.show_swipe_num_all_messages);
|
||||
}
|
||||
|
||||
var originalSliderValues = [];
|
||||
@@ -1481,6 +1483,7 @@ async function loadPowerUserSettings(settings, data) {
|
||||
$('#example_messages_behavior').val(getExampleMessagesBehavior());
|
||||
$(`#example_messages_behavior option[value="${getExampleMessagesBehavior()}"]`).prop('selected', true);
|
||||
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
|
||||
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
|
||||
|
||||
$('#console_log_prompts').prop('checked', power_user.console_log_prompts);
|
||||
$('#request_token_probabilities').prop('checked', power_user.request_token_probabilities);
|
||||
@@ -1754,7 +1757,7 @@ async function loadContextSettings() {
|
||||
} else {
|
||||
$element.val(power_user.context[control.property]);
|
||||
}
|
||||
console.log(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
|
||||
console.debug(`Setting ${$element.prop('id')} to ${power_user.context[control.property]}`);
|
||||
|
||||
// If the setting already exists, no need to duplicate it
|
||||
// TODO: Maybe check the power_user object for the setting instead of a flag?
|
||||
@@ -1765,7 +1768,7 @@ async function loadContextSettings() {
|
||||
} else {
|
||||
power_user.context[control.property] = value;
|
||||
}
|
||||
console.log(`Setting ${$element.prop('id')} to ${value}`);
|
||||
console.debug(`Setting ${$element.prop('id')} to ${value}`);
|
||||
if (!CSS.supports('field-sizing', 'content') && $(this).is('textarea')) {
|
||||
await resetScrollHeight($(this));
|
||||
}
|
||||
@@ -1829,27 +1832,27 @@ async function loadContextSettings() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fuzzy search characters by a search term
|
||||
* Common function to perform fuzzy search with optional caching
|
||||
* @param {string} type - Type of search from fuzzySearchCategories
|
||||
* @param {any[]} data - Data array to search in
|
||||
* @param {Array<{name: string, weight: number, getFn?: (obj: any) => string}>} keys - Fuse.js keys configuration
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchCharacters(searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(characters, {
|
||||
keys: [
|
||||
{ name: 'data.name', weight: 20 },
|
||||
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
|
||||
{ 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 },
|
||||
],
|
||||
function performFuzzySearch(type, data, keys, searchValue, fuzzySearchCaches = null) {
|
||||
// Check cache if provided
|
||||
if (fuzzySearchCaches) {
|
||||
const cache = fuzzySearchCaches[type];
|
||||
if (cache?.resultMap.has(searchValue)) {
|
||||
return cache.resultMap.get(searchValue);
|
||||
}
|
||||
}
|
||||
|
||||
const fuse = new Fuse(data, {
|
||||
keys: keys,
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
@@ -1857,109 +1860,110 @@ export function fuzzySearchCharacters(searchValue) {
|
||||
});
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Characters fuzzy search results for ' + searchValue, results);
|
||||
|
||||
// Store in cache if provided
|
||||
if (fuzzySearchCaches) {
|
||||
fuzzySearchCaches[type].resultMap.set(searchValue, results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search characters by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchCharacters(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'data.name', weight: 20 },
|
||||
{ name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') },
|
||||
{ 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 },
|
||||
];
|
||||
|
||||
return performFuzzySearch(fuzzySearchCategories.characters, characters, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search world info entries by a search term
|
||||
* @param {*[]} data - WI items data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchWorldInfo(data, searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: [
|
||||
{ name: 'key', weight: 20 },
|
||||
{ name: 'group', weight: 15 },
|
||||
{ name: 'comment', weight: 10 },
|
||||
{ name: 'keysecondary', weight: 10 },
|
||||
{ name: 'content', weight: 3 },
|
||||
{ name: 'uid', weight: 1 },
|
||||
{ name: 'automationId', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchWorldInfo(data, searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'key', weight: 20 },
|
||||
{ name: 'group', weight: 15 },
|
||||
{ name: 'comment', weight: 10 },
|
||||
{ name: 'keysecondary', weight: 10 },
|
||||
{ name: 'content', weight: 3 },
|
||||
{ name: 'uid', weight: 1 },
|
||||
{ name: 'automationId', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('World Info fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.worldInfo, data, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search persona entries by a search term
|
||||
* @param {*[]} data - persona data array
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchPersonas(data, searchValue) {
|
||||
data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' }));
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(data, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'description', weight: 3 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchPersonas(data, searchValue, fuzzySearchCaches = null) {
|
||||
const mappedData = data.map(x => ({
|
||||
key: x,
|
||||
name: power_user.personas[x] ?? '',
|
||||
description: power_user.persona_descriptions[x]?.description ?? '',
|
||||
}));
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Personas fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
const keys = [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'description', weight: 3 },
|
||||
];
|
||||
|
||||
return performFuzzySearch(fuzzySearchCategories.personas, mappedData, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search tags by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchTags(searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(tags, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchTags(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'name', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Tags fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.tags, tags, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuzzy search groups by a search term
|
||||
* @param {string} searchValue - The search term
|
||||
* @param {Object.<string, { resultMap: Map<string, any> }>} [fuzzySearchCaches=null] - Optional fuzzy search caches
|
||||
* @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score
|
||||
*/
|
||||
export function fuzzySearchGroups(searchValue) {
|
||||
// @ts-ignore
|
||||
const fuse = new Fuse(groups, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'members', weight: 15 },
|
||||
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
|
||||
{ name: 'id', weight: 1 },
|
||||
],
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
export function fuzzySearchGroups(searchValue, fuzzySearchCaches = null) {
|
||||
const keys = [
|
||||
{ name: 'name', weight: 20 },
|
||||
{ name: 'members', weight: 15 },
|
||||
{ name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') },
|
||||
{ name: 'id', weight: 1 },
|
||||
];
|
||||
|
||||
const results = fuse.search(searchValue);
|
||||
console.debug('Groups fuzzy search results for ' + searchValue, results);
|
||||
return results;
|
||||
return performFuzzySearch(fuzzySearchCategories.groups, groups, keys, searchValue, fuzzySearchCaches);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2075,19 +2079,22 @@ const compareFunc = (first, second) => {
|
||||
/**
|
||||
* Sorts an array of entities based on the current sort settings
|
||||
* @param {any[]} entities An array of objects with an `item` property
|
||||
* @param {boolean} forceSearch Whether to force search sorting
|
||||
* @param {import('./filters.js').FilterHelper} [filterHelper=null] Filter helper to use
|
||||
*/
|
||||
function sortEntitiesList(entities) {
|
||||
export function sortEntitiesList(entities, forceSearch, filterHelper = null) {
|
||||
filterHelper = filterHelper ?? entitiesFilter;
|
||||
if (power_user.sort_field == undefined || entities.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (power_user.sort_order === 'random') {
|
||||
const isSearch = forceSearch || $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
if (!isSearch && power_user.sort_order === 'random') {
|
||||
shuffle(entities);
|
||||
return;
|
||||
}
|
||||
|
||||
const isSearch = $('#character_sort_order option[data-field="search"]').is(':selected');
|
||||
|
||||
entities.sort((a, b) => {
|
||||
// Sort tags/folders will always be at the top
|
||||
if (a.type === 'tag' && b.type !== 'tag') {
|
||||
@@ -2099,8 +2106,8 @@ function sortEntitiesList(entities) {
|
||||
|
||||
// If we have search sorting, we take scores and use those
|
||||
if (isSearch) {
|
||||
const aScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = entitiesFilter.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
const aScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${a.type}.${a.id}`);
|
||||
const bScore = filterHelper.getScore(FILTER_TYPES.SEARCH, `${b.type}.${b.id}`);
|
||||
return (aScore - bScore);
|
||||
}
|
||||
|
||||
@@ -3076,6 +3083,16 @@ $(document).ready(() => {
|
||||
$('#context_derived').parent().find('i').toggleClass('toggleEnabled', !!power_user.context_derived);
|
||||
});
|
||||
|
||||
$('#context_size_derived').on('input', function () {
|
||||
const value = !!$(this).prop('checked');
|
||||
power_user.context_size_derived = value;
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#context_size_derived').on('change', function () {
|
||||
$('#context_size_derived').prop('checked', !!power_user.context_size_derived);
|
||||
});
|
||||
|
||||
$('#always-force-name2-checkbox').change(function () {
|
||||
power_user.always_force_name2 = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@@ -585,6 +585,7 @@ class PresetManager {
|
||||
'openrouter_allow_fallbacks',
|
||||
'tabby_model',
|
||||
'derived',
|
||||
'generic_model',
|
||||
];
|
||||
const settings = Object.assign({}, getSettingsByApiId(this.apiId));
|
||||
|
||||
|
@@ -129,6 +129,10 @@ function setSamplerListListeners() {
|
||||
relatedDOMElement = $('#sampler_priority_block_ooba');
|
||||
}
|
||||
|
||||
if (samplerName === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_aphrodite');
|
||||
}
|
||||
|
||||
if (samplerName === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
|
||||
relatedDOMElement = $('#contrastiveSearchBlock');
|
||||
}
|
||||
@@ -237,6 +241,11 @@ async function listSamplers(main_api, arrayOnly = false) {
|
||||
displayname = 'Ooba Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
targetDOMelement = $('#sampler_priority_block_aphrodite');
|
||||
displayname = 'Aphrodite Sampler Priority Block';
|
||||
}
|
||||
|
||||
if (sampler === 'penalty_alpha') { //contrastive search only has one sampler, does it need its own block?
|
||||
targetDOMelement = $('#contrastiveSearchBlock');
|
||||
displayname = 'Contrast Search Block';
|
||||
@@ -373,6 +382,10 @@ export async function validateDisabledSamplers(redraw = false) {
|
||||
relatedDOMElement = $('#sampler_priority_block_ooba');
|
||||
}
|
||||
|
||||
if (sampler === 'samplers_priorities') { //this is for aphrodite's sampler priority
|
||||
relatedDOMElement = $('#sampler_priority_block_aphrodite');
|
||||
}
|
||||
|
||||
if (sampler === 'dry_multiplier') {
|
||||
relatedDOMElement = $('#dryBlock');
|
||||
targetDisplayType = 'block';
|
||||
|
@@ -38,6 +38,8 @@ export const SECRET_KEYS = {
|
||||
NANOGPT: 'api_key_nanogpt',
|
||||
TAVILY: 'api_key_tavily',
|
||||
BFL: 'api_key_bfl',
|
||||
GENERIC: 'api_key_generic',
|
||||
DEEPSEEK: 'api_key_deepseek',
|
||||
};
|
||||
|
||||
const INPUT_MAP = {
|
||||
@@ -71,6 +73,8 @@ const INPUT_MAP = {
|
||||
[SECRET_KEYS.HUGGINGFACE]: '#api_key_huggingface',
|
||||
[SECRET_KEYS.BLOCKENTROPY]: '#api_key_blockentropy',
|
||||
[SECRET_KEYS.NANOGPT]: '#api_key_nanogpt',
|
||||
[SECRET_KEYS.GENERIC]: '#api_key_generic',
|
||||
[SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek',
|
||||
};
|
||||
|
||||
async function clearSecret() {
|
||||
|
@@ -47,12 +47,12 @@ import {
|
||||
} from '../script.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
|
||||
import { getMessageTimeStamp } from './RossAscends-mods.js';
|
||||
import { getMessageTimeStamp, isMobile } from './RossAscends-mods.js';
|
||||
import { hideChatMessageRange } from './chats.js';
|
||||
import { getContext, saveMetadataDebounced } from './extensions.js';
|
||||
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
|
||||
import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
|
||||
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
|
||||
import { chat_completion_sources, oai_settings, promptManager } from './openai.js';
|
||||
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
|
||||
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
|
||||
import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
|
||||
@@ -84,6 +84,25 @@ export const parser = new SlashCommandParser();
|
||||
const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser);
|
||||
const getSlashCommandsHelp = parser.getHelpString.bind(parser);
|
||||
|
||||
/**
|
||||
* Converts a SlashCommandClosure to a filter function that returns a boolean.
|
||||
* @param {SlashCommandClosure} closure
|
||||
* @returns {() => Promise<boolean>}
|
||||
*/
|
||||
function closureToFilter(closure) {
|
||||
return async () => {
|
||||
try {
|
||||
const localClosure = closure.getCopy();
|
||||
localClosure.onProgress = () => { };
|
||||
const result = await localClosure.execute();
|
||||
return isTrueBoolean(result.pipe);
|
||||
} catch (e) {
|
||||
console.error('Error executing filter closure', e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function initDefaultSlashCommands() {
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: '?',
|
||||
@@ -269,7 +288,7 @@ export function initDefaultSlashCommands() {
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -325,7 +344,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -388,7 +407,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -606,7 +625,7 @@ export function initDefaultSlashCommands() {
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'at',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values (including -0) are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.',
|
||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
@@ -1611,6 +1630,13 @@ export function initDefaultSlashCommands() {
|
||||
new SlashCommandNamedArgument(
|
||||
'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
|
||||
),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'filter',
|
||||
description: 'if a filter is defined, an injection will only be performed if the closure returns true',
|
||||
typeList: [ARGUMENT_TYPE.CLOSURE],
|
||||
isRequired: false,
|
||||
acceptsMultiple: false,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
@@ -1698,6 +1724,49 @@ export function initDefaultSlashCommands() {
|
||||
],
|
||||
helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'getpromptentry',
|
||||
aliases: ['getpromptentries'],
|
||||
callback: getPromptEntryCallback,
|
||||
returns: 'true/false state of prompt(s)',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'identifier',
|
||||
description: 'Prompt entry identifier(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.identifier)
|
||||
.map(identifier => new SlashCommandEnumValue(identifier)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
description: 'Prompt entry name(s) to retrieve',
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () =>
|
||||
promptManager.serviceSettings.prompts
|
||||
.map(prompt => prompt.name)
|
||||
.map(name => new SlashCommandEnumValue(name)),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'Whether the return will be simple, a list, or a dict.',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'simple',
|
||||
enumList: ['simple', 'list', 'dict'],
|
||||
}),
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Gets the state of the specified prompt entries.
|
||||
</div>
|
||||
<div>
|
||||
If <code>return</code> is <code>simple</code> (default) then the return will be a single value if only one value was retrieved; otherwise uses a dict (if the identifier parameter was used) or a list.
|
||||
</div>
|
||||
`,
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'setpromptentry',
|
||||
aliases: ['setpromptentries'],
|
||||
@@ -1709,7 +1778,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum));
|
||||
},
|
||||
@@ -1720,7 +1788,6 @@ export function initDefaultSlashCommands() {
|
||||
typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
|
||||
acceptsMultiple: true,
|
||||
enumProvider: () => {
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum));
|
||||
},
|
||||
@@ -1851,6 +1918,52 @@ export function initDefaultSlashCommands() {
|
||||
],
|
||||
helpString: 'Converts the provided string to lowercase.',
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'substr',
|
||||
aliases: ['substring'],
|
||||
callback: (arg, text) => typeof text === 'string' ? text.slice(...[Number(arg.start), arg.end && Number(arg.end)]) : '',
|
||||
returns: 'substring',
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument(
|
||||
'start', 'start index', [ARGUMENT_TYPE.NUMBER], false, false,
|
||||
),
|
||||
new SlashCommandNamedArgument(
|
||||
'end', 'end index', [ARGUMENT_TYPE.NUMBER], false, false,
|
||||
),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
'string', [ARGUMENT_TYPE.STRING], true, false,
|
||||
),
|
||||
],
|
||||
helpString: `
|
||||
<div>
|
||||
Extracts text from the provided string.
|
||||
</div>
|
||||
<div>
|
||||
If <code>start</code> is omitted, it's treated as 0.<br />
|
||||
If <code>start</code> < 0, the index is counted from the end of the string.<br />
|
||||
If <code>start</code> >= the string's length, an empty string is returned.<br />
|
||||
If <code>end</code> is omitted, or if <code>end</code> >= the string's length, extracts to the end of the string.<br />
|
||||
If <code>end</code> < 0, the index is counted from the end of the string.<br />
|
||||
If <code>end</code> <= <code>start</code> after normalizing negative values, an empty string is returned.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Example:</strong>
|
||||
<pre>/let x The morning is upon us. || </pre>
|
||||
<pre>/substr start=-3 {{var::x}} | /echo |/# us. ||</pre>
|
||||
<pre>/substr start=-3 end=-1 {{var::x}} | /echo |/# us ||</pre>
|
||||
<pre>/substr end=-1 {{var::x}} | /echo |/# The morning is upon us ||</pre>
|
||||
<pre>/substr start=4 end=-1 {{var::x}} | /echo |/# morning is upon us ||</pre>
|
||||
</div>
|
||||
`,
|
||||
}));
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'is-mobile',
|
||||
callback: () => String(isMobile()),
|
||||
returns: ARGUMENT_TYPE.BOOLEAN,
|
||||
helpString: 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.',
|
||||
}));
|
||||
|
||||
registerVariableCommands();
|
||||
}
|
||||
@@ -1860,6 +1973,11 @@ const NARRATOR_NAME_DEFAULT = 'System';
|
||||
export const COMMENT_NAME_DEFAULT = 'Note';
|
||||
const SCRIPT_PROMPT_KEY = 'script_inject_';
|
||||
|
||||
/**
|
||||
* Adds a new script injection to the chat.
|
||||
* @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
|
||||
* @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value Unnamed argument
|
||||
*/
|
||||
function injectCallback(args, value) {
|
||||
const positions = {
|
||||
'before': extension_prompt_types.BEFORE_PROMPT,
|
||||
@@ -1873,8 +1991,8 @@ function injectCallback(args, value) {
|
||||
'assistant': extension_prompt_roles.ASSISTANT,
|
||||
};
|
||||
|
||||
const id = args?.id;
|
||||
const ephemeral = isTrueBoolean(args?.ephemeral);
|
||||
const id = String(args?.id);
|
||||
const ephemeral = isTrueBoolean(String(args?.ephemeral));
|
||||
|
||||
if (!id) {
|
||||
console.warn('WARN: No ID provided for /inject command');
|
||||
@@ -1890,9 +2008,15 @@ function injectCallback(args, value) {
|
||||
const depth = isNaN(depthValue) ? defaultDepth : depthValue;
|
||||
const roleValue = typeof args?.role === 'string' ? args.role.toLowerCase().trim() : Number(args?.role ?? extension_prompt_roles.SYSTEM);
|
||||
const role = roles[roleValue] ?? roles[extension_prompt_roles.SYSTEM];
|
||||
const scan = isTrueBoolean(args?.scan);
|
||||
const scan = isTrueBoolean(String(args?.scan));
|
||||
const filter = args?.filter instanceof SlashCommandClosure ? args.filter.rawText : null;
|
||||
const filterFunction = args?.filter instanceof SlashCommandClosure ? closureToFilter(args.filter) : null;
|
||||
value = value || '';
|
||||
|
||||
if (args?.filter && !String(filter ?? '').trim()) {
|
||||
throw new Error('Failed to parse the filter argument. Make sure it is a valid non-empty closure.');
|
||||
}
|
||||
|
||||
const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
|
||||
|
||||
if (!chat_metadata.script_injects) {
|
||||
@@ -1900,13 +2024,13 @@ function injectCallback(args, value) {
|
||||
}
|
||||
|
||||
if (value) {
|
||||
const inject = { value, position, depth, scan, role };
|
||||
const inject = { value, position, depth, scan, role, filter };
|
||||
chat_metadata.script_injects[id] = inject;
|
||||
} else {
|
||||
delete chat_metadata.script_injects[id];
|
||||
}
|
||||
|
||||
setExtensionPrompt(prefixedId, value, position, depth, scan, role);
|
||||
setExtensionPrompt(prefixedId, String(value), position, depth, scan, role, filterFunction);
|
||||
saveMetadataDebounced();
|
||||
|
||||
if (ephemeral) {
|
||||
@@ -1917,7 +2041,7 @@ function injectCallback(args, value) {
|
||||
}
|
||||
console.log('Removing ephemeral script injection', id);
|
||||
delete chat_metadata.script_injects[id];
|
||||
setExtensionPrompt(prefixedId, '', position, depth, scan, role);
|
||||
setExtensionPrompt(prefixedId, '', position, depth, scan, role, filterFunction);
|
||||
saveMetadataDebounced();
|
||||
deleted = true;
|
||||
};
|
||||
@@ -2012,9 +2136,28 @@ export function processChatSlashCommands() {
|
||||
}
|
||||
|
||||
for (const [id, inject] of Object.entries(context.chatMetadata.script_injects)) {
|
||||
/**
|
||||
* Rehydrates a filter closure from a string.
|
||||
* @returns {SlashCommandClosure | null}
|
||||
*/
|
||||
function reviveFilterClosure() {
|
||||
if (!inject.filter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new SlashCommandParser().parse(inject.filter, true);
|
||||
} catch (error) {
|
||||
console.warn('Failed to revive filter closure for script injection', id, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const prefixedId = `${SCRIPT_PROMPT_KEY}${id}`;
|
||||
const filterClosure = reviveFilterClosure();
|
||||
const filter = filterClosure ? closureToFilter(filterClosure) : null;
|
||||
console.log('Adding script injection', id);
|
||||
setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role);
|
||||
setExtensionPrompt(prefixedId, inject.value, inject.position, inject.depth, inject.scan, inject.role, filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3025,7 +3168,7 @@ async function sendUserMessageCallback(args, text) {
|
||||
let insertAt = Number(args?.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3399,7 +3542,7 @@ export async function sendMessageAs(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3453,7 +3596,7 @@ export async function sendNarratorMessage(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3542,7 +3685,7 @@ async function sendCommentMessage(args, text) {
|
||||
let insertAt = Number(args.at);
|
||||
|
||||
// Convert possible depth parameter to index
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) {
|
||||
if (!isNaN(insertAt) && (insertAt < 0 || Object.is(insertAt, -0))) {
|
||||
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
|
||||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
@@ -3646,6 +3789,7 @@ function setBackgroundCallback(_, bg) {
|
||||
function getModelOptions(quiet) {
|
||||
const nullResult = { control: null, options: null };
|
||||
const modelSelectMap = [
|
||||
{ id: 'generic_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.GENERIC },
|
||||
{ id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA },
|
||||
{ id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },
|
||||
{ id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },
|
||||
@@ -3671,6 +3815,7 @@ function getModelOptions(quiet) {
|
||||
{ id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT },
|
||||
{ id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI },
|
||||
{ id: 'model_blockentropy_select', api: 'openai', type: chat_completion_sources.BLOCKENTROPY },
|
||||
{ id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK },
|
||||
{ id: 'model_novel_select', api: 'novel', type: null },
|
||||
{ id: 'horde_model', api: 'koboldhorde', type: null },
|
||||
];
|
||||
@@ -3786,6 +3931,75 @@ function modelCallback(args, model) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
* @param {string} args.identifier Select prompt entry using an identifier (uuid)
|
||||
* @param {string} args.name Select prompt entry using name
|
||||
* @param {string} args.return The type of return value to use (simple, list, dict)
|
||||
* @returns {Object} An object containing the states of the requested prompt entries
|
||||
*/
|
||||
function getPromptEntryCallback(args) {
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
let returnType = args.return ?? 'simple';
|
||||
|
||||
function parseArgs(arg) {
|
||||
// Arg is already an array
|
||||
if (Array.isArray(arg)) {
|
||||
return arg;
|
||||
}
|
||||
const list = [];
|
||||
try {
|
||||
// Arg is a JSON-stringified array
|
||||
const parsedArg = JSON.parse(arg);
|
||||
list.push(...Array.isArray(parsedArg) ? parsedArg : [arg]);
|
||||
} catch {
|
||||
// Arg is a string
|
||||
list.push(arg);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
let identifiersList = parseArgs(args.identifier);
|
||||
let nameList = parseArgs(args.name);
|
||||
|
||||
// Check if identifiers exists in prompt, else remove from list
|
||||
if (identifiersList.length !== 0) {
|
||||
identifiersList = identifiersList.filter(identifier => prompts.some(prompt => prompt.identifier === identifier));
|
||||
}
|
||||
|
||||
if (nameList.length !== 0) {
|
||||
nameList.forEach(name => {
|
||||
let identifiers = prompts
|
||||
.filter(entry => entry.name === name)
|
||||
.map(entry => entry.identifier);
|
||||
identifiersList = identifiersList.concat(identifiers);
|
||||
});
|
||||
}
|
||||
|
||||
// Get the state for each prompt entry
|
||||
let promptStates = new Map();
|
||||
identifiersList.forEach(identifier => {
|
||||
const promptOrderEntry = promptManager.getPromptOrderEntry(promptManager.activeCharacter, identifier);
|
||||
if (promptOrderEntry) {
|
||||
promptStates.set(identifier, promptOrderEntry.enabled);
|
||||
}
|
||||
});
|
||||
|
||||
// If return is simple (default) but more than one prompt state was retrieved, then change return type
|
||||
if (returnType === 'simple' && promptStates.size > 1) {
|
||||
returnType = args.identifier ? 'dict' : 'list';
|
||||
}
|
||||
|
||||
const result = (() => {
|
||||
if (returnType === 'list') return [...promptStates.values()];
|
||||
if (returnType === 'dict') return Object.fromEntries(promptStates);
|
||||
return [...promptStates.values()][0];
|
||||
})();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state of prompt entries (toggles) either via identifier/uuid or name.
|
||||
* @param {object} args Object containing arguments
|
||||
@@ -3796,7 +4010,6 @@ function modelCallback(args, model) {
|
||||
*/
|
||||
function setPromptEntryCallback(args, targetState) {
|
||||
// needs promptManager to manipulate prompt entries
|
||||
const promptManager = setupChatCompletionPromptManager(oai_settings);
|
||||
const prompts = promptManager.serviceSettings.prompts;
|
||||
|
||||
function parseArgs(arg) {
|
||||
|
@@ -144,9 +144,14 @@ async function* parseStreamData(json) {
|
||||
for (let j = 0; j < json.candidates[i].content.parts.length; j++) {
|
||||
if (typeof json.candidates[i].content.parts[j].text === 'string') {
|
||||
for (let k = 0; k < json.candidates[i].content.parts[j].text.length; k++) {
|
||||
const str = json.candidates[i].content.parts[j].text[k];
|
||||
const moreThanOnePart = json.candidates[i].content.parts.length > 1;
|
||||
const isNotLastPart = j !== json.candidates[i].content.parts.length - 1;
|
||||
const isLastSymbol = k === json.candidates[i].content.parts[j].text.length - 1;
|
||||
const addNewline = moreThanOnePart && isNotLastPart && isLastSymbol;
|
||||
const str = json.candidates[i].content.parts[j].text[k] + (addNewline ? '\n\n' : '');
|
||||
const candidateClone = structuredClone(json.candidates[0]);
|
||||
candidateClone.content.parts[j].text = str;
|
||||
candidateClone.content.parts = [candidateClone.content.parts[j]];
|
||||
const candidates = [candidateClone];
|
||||
yield {
|
||||
data: { ...json, candidates },
|
||||
|
173
public/scripts/st-context.js
Normal file
173
public/scripts/st-context.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import {
|
||||
activateSendButtons,
|
||||
addOneMessage,
|
||||
callPopup,
|
||||
characters,
|
||||
chat,
|
||||
chat_metadata,
|
||||
create_save,
|
||||
deactivateSendButtons,
|
||||
event_types,
|
||||
eventSource,
|
||||
extension_prompts,
|
||||
Generate,
|
||||
generateQuietPrompt,
|
||||
getCurrentChatId,
|
||||
getRequestHeaders,
|
||||
getThumbnailUrl,
|
||||
main_api,
|
||||
max_context,
|
||||
menu_type,
|
||||
messageFormatting,
|
||||
name1,
|
||||
name2,
|
||||
online_status,
|
||||
openCharacterChat,
|
||||
reloadCurrentChat,
|
||||
renameChat,
|
||||
saveChatConditional,
|
||||
saveMetadata,
|
||||
saveReply,
|
||||
saveSettingsDebounced,
|
||||
selectCharacterById,
|
||||
sendGenerationRequest,
|
||||
sendStreamingRequest,
|
||||
sendSystemMessage,
|
||||
setExtensionPrompt,
|
||||
stopGeneration,
|
||||
streamingProcessor,
|
||||
substituteParams,
|
||||
substituteParamsExtended,
|
||||
this_chid,
|
||||
updateChatMetadata,
|
||||
} from '../script.js';
|
||||
import {
|
||||
extension_settings,
|
||||
ModuleWorkerWrapper,
|
||||
renderExtensionTemplate,
|
||||
renderExtensionTemplateAsync,
|
||||
writeExtensionField,
|
||||
} from './extensions.js';
|
||||
import { groups, openGroupChat, selected_group } from './group-chats.js';
|
||||
import { t, translate } from './i18n.js';
|
||||
import { hideLoader, showLoader } from './loader.js';
|
||||
import { MacrosParser } from './macros.js';
|
||||
import { oai_settings } from './openai.js';
|
||||
import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
||||
import { power_user, registerDebugFunction } from './power-user.js';
|
||||
import { isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
|
||||
import { ScraperManager } from './scrapers.js';
|
||||
import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';
|
||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { tag_map, tags } from './tags.js';
|
||||
import { textgenerationwebui_settings } from './textgen-settings.js';
|
||||
import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
|
||||
import { ToolManager } from './tool-calling.js';
|
||||
import { timestampToMoment } from './utils.js';
|
||||
|
||||
export function getContext() {
|
||||
return {
|
||||
chat,
|
||||
characters,
|
||||
groups,
|
||||
name1,
|
||||
name2,
|
||||
characterId: this_chid,
|
||||
groupId: selected_group,
|
||||
chatId: selected_group
|
||||
? groups.find(x => x.id == selected_group)?.chat_id
|
||||
: (characters[this_chid]?.chat),
|
||||
getCurrentChatId,
|
||||
getRequestHeaders,
|
||||
reloadCurrentChat,
|
||||
renameChat,
|
||||
saveSettingsDebounced,
|
||||
onlineStatus: online_status,
|
||||
maxContext: Number(max_context),
|
||||
chatMetadata: chat_metadata,
|
||||
streamingProcessor,
|
||||
eventSource,
|
||||
eventTypes: event_types,
|
||||
addOneMessage,
|
||||
generate: Generate,
|
||||
sendStreamingRequest,
|
||||
sendGenerationRequest,
|
||||
stopGeneration,
|
||||
tokenizers,
|
||||
getTextTokens,
|
||||
/** @deprecated Use getTokenCountAsync instead */
|
||||
getTokenCount,
|
||||
getTokenCountAsync,
|
||||
extensionPrompts: extension_prompts,
|
||||
setExtensionPrompt,
|
||||
updateChatMetadata,
|
||||
saveChat: saveChatConditional,
|
||||
openCharacterChat,
|
||||
openGroupChat,
|
||||
saveMetadata,
|
||||
sendSystemMessage,
|
||||
activateSendButtons,
|
||||
deactivateSendButtons,
|
||||
saveReply,
|
||||
substituteParams,
|
||||
substituteParamsExtended,
|
||||
SlashCommandParser,
|
||||
SlashCommand,
|
||||
SlashCommandArgument,
|
||||
SlashCommandNamedArgument,
|
||||
ARGUMENT_TYPE,
|
||||
executeSlashCommandsWithOptions,
|
||||
/** @deprecated Use SlashCommandParser.addCommandObject() instead */
|
||||
registerSlashCommand,
|
||||
/** @deprecated Use executeSlashCommandWithOptions instead */
|
||||
executeSlashCommands,
|
||||
timestampToMoment,
|
||||
/** @deprecated Handlebars for extensions are no longer supported. */
|
||||
registerHelper: () => { },
|
||||
registerMacro: MacrosParser.registerMacro.bind(MacrosParser),
|
||||
unregisterMacro: MacrosParser.unregisterMacro.bind(MacrosParser),
|
||||
registerFunctionTool: ToolManager.registerFunctionTool.bind(ToolManager),
|
||||
unregisterFunctionTool: ToolManager.unregisterFunctionTool.bind(ToolManager),
|
||||
isToolCallingSupported: ToolManager.isToolCallingSupported.bind(ToolManager),
|
||||
canPerformToolCalls: ToolManager.canPerformToolCalls.bind(ToolManager),
|
||||
registerDebugFunction,
|
||||
/** @deprecated Use renderExtensionTemplateAsync instead. */
|
||||
renderExtensionTemplate,
|
||||
renderExtensionTemplateAsync,
|
||||
registerDataBankScraper: ScraperManager.registerDataBankScraper.bind(ScraperManager),
|
||||
/** @deprecated Use callGenericPopup or Popup instead. */
|
||||
callPopup,
|
||||
callGenericPopup,
|
||||
showLoader,
|
||||
hideLoader,
|
||||
mainApi: main_api,
|
||||
extensionSettings: extension_settings,
|
||||
ModuleWorkerWrapper,
|
||||
getTokenizerModel,
|
||||
generateQuietPrompt,
|
||||
writeExtensionField,
|
||||
getThumbnailUrl,
|
||||
selectCharacterById,
|
||||
messageFormatting,
|
||||
shouldSendOnEnter,
|
||||
isMobile,
|
||||
t,
|
||||
translate,
|
||||
tags,
|
||||
tagMap: tag_map,
|
||||
menuType: menu_type,
|
||||
createCharacterData: create_save,
|
||||
/** @deprecated Legacy snake-case naming, compatibility with old extensions */
|
||||
event_types: event_types,
|
||||
Popup,
|
||||
POPUP_TYPE,
|
||||
POPUP_RESULT,
|
||||
chatCompletionSettings: oai_settings,
|
||||
textCompletionSettings: textgenerationwebui_settings,
|
||||
powerUserSettings: power_user,
|
||||
};
|
||||
}
|
||||
|
||||
export default getContext;
|
@@ -1,5 +1,5 @@
|
||||
<div class="flex-container">
|
||||
<h3>
|
||||
<h3 data-i18n="Enter a new display name:">
|
||||
Enter a new display name:
|
||||
</h3>
|
||||
</div>
|
||||
|
18
public/scripts/templates/chatLorebook.html
Normal file
18
public/scripts/templates/chatLorebook.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="chat_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h4 data-i18n="Chat Lorebook"><!-- This data-i18n attribute is kept for backward compatibility, use the ones below when translating -->
|
||||
<span data-i18n="Chat Lorebook for">Chat Lorebook for</span> <span class="chat_name"></span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
|
||||
<span data-i18n="chat_world_template_txt">
|
||||
A selected World Info will be bound to this chat. When generating an AI reply,
|
||||
it will be combined with the entries from global and character lorebooks.
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range wide100p">
|
||||
<select class="chat_world_info_selector wide100p">
|
||||
<option value="">--- None ---</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
@@ -1,8 +1,8 @@
|
||||
<div>
|
||||
<span class="margin-right-10px">Enter Checkpoint Name:</span><small>(Leave empty to auto-generate)</small>
|
||||
<span class="margin-right-10px" data-i18n="Enter Checkpoint Name:">Enter Checkpoint Name:</span><small data-i18n="(Leave empty to auto-generate)">(Leave empty to auto-generate)</small>
|
||||
</div>
|
||||
{{#if isReplace}}
|
||||
<div class="m-t-1">
|
||||
<small>The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.</small>
|
||||
<small data-i18n="The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.">The currently existing checkpoint will be unlinked and replaced with the new checkpoint, but can still be found in the Chat Management.</small>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div>
|
||||
<h3 data-i18n="Debug Menu">Debug Menu</h3>
|
||||
<div data-i18n="Debug Warning">
|
||||
<div data-i18n="Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.">
|
||||
Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
|
||||
</div>
|
||||
<table id="debug_table" class="responsiveTable">
|
||||
|
@@ -1,7 +1,10 @@
|
||||
<h3>Enter the Git URL of the extension to install</h3>
|
||||
<h3 data-i18n="Enter the Git URL of the extension to install">Enter the Git URL of the extension to install</h3>
|
||||
<br>
|
||||
<p><b>Disclaimer:</b> Please be aware that using external extensions can have unintended side effects and may pose
|
||||
security risks. Always make sure you trust the source before importing an extension. We are not responsible for any
|
||||
damage caused by third-party extensions.</p>
|
||||
<p>
|
||||
<b data-i18n="Disclaimer:">Disclaimer:</b>
|
||||
<span data-i18n="Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.">
|
||||
Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.
|
||||
</span>
|
||||
</p>
|
||||
<br>
|
||||
<p>Example: <tt> https://github.com/author/extension-name </tt></p>
|
||||
<p><span data-i18n="Example:">Example:</span> <tt> https://github.com/author/extension-name </tt></p>
|
||||
|
@@ -1,12 +1,21 @@
|
||||
<h3 class="flex-container justifyCenter alignitemscenter">
|
||||
Prompt Itemization
|
||||
<span data-i18n="Prompt Itemization">Prompt Itemization</span>
|
||||
<div id="showRawPrompt" class="fa-solid fa-square-poll-horizontal menu_button" title="Show Raw Prompt" data-i18n="[title]Show Raw Prompt"></div>
|
||||
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
|
||||
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
|
||||
</h3>
|
||||
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br>
|
||||
Tokenizer: {{selectedTokenizer}}<br>
|
||||
<span class="tokenItemizingSubclass">
|
||||
<div>
|
||||
<div>
|
||||
<span data-i18n="API/Model:">API/Model:</span> {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<small><span data-i18n="Preset:">Preset:</span> {{presetName}}</small>
|
||||
<span>|</span>
|
||||
<small><span data-i18n="Tokenizer:">Tokenizer:</span> {{selectedTokenizer}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="tokenItemizingSubclass" data-i18n="Only the white numbers really matter. All numbers are estimates. Grey color items may not have been included in the context due to certain prompt format settings.">
|
||||
Only the white numbers really matter. All numbers are estimates.
|
||||
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||
</span>
|
||||
@@ -28,12 +37,12 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
<div class="flex-container wide50p">
|
||||
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||
<div class="flex-container wide100p">
|
||||
<div class="flex1" style="color: grey;">System Info:</div>
|
||||
<div class=""> {{oaiSystemTokens}}</div>
|
||||
<div class="flex1" style="color: grey;"><span data-i18n="System Info:">System Info:</span></div>
|
||||
<div class="">{{oaiSystemTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Chat Start: </div>
|
||||
<div class="tokenItemizingSubclass"> {{oaiStartTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{oaiStartTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Main: </div>
|
||||
@@ -58,8 +67,8 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
</div>
|
||||
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||
<div class="flex-container wide100p">
|
||||
<div class="flex1" style="color: indianred;">Prompt Tokens:</div>
|
||||
<div class=""> {{oaiPromptTokens}}</div>
|
||||
<div class="flex1" style="color: indianred;"><span data-i18n="Prompt Tokens:">Prompt Tokens:</span></div>
|
||||
<div class="">{{oaiPromptTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Description: </div>
|
||||
@@ -67,7 +76,7 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Personality:</div>
|
||||
<div class="tokenItemizingSubclass"> {{charPersonalityTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{charPersonalityTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Scenario: </div>
|
||||
@@ -75,24 +84,24 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Examples:</div>
|
||||
<div class="tokenItemizingSubclass"> {{examplesStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{examplesStringTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- User Persona:</div>
|
||||
<div class="tokenItemizingSubclass"> {{userPersonaStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{userPersonaStringTokens}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wide100p flex-container">
|
||||
<div class="flex1" style="color: gold;">World Info:</div>
|
||||
<div class="flex1" style="color: gold;"><span data-i18n="World Info:">World Info:</span></div>
|
||||
<div class="">{{worldInfoStringTokens}}</div>
|
||||
</div>
|
||||
<div class="wide100p flex-container">
|
||||
<div class="flex1" style="color: palegreen;">Chat History:</div>
|
||||
<div class=""> {{ActualChatHistoryTokens}}</div>
|
||||
<div class="flex1" style="color: palegreen;"><span data-i18n="Chat History:">Chat History:</span></div>
|
||||
<div class="">{{ActualChatHistoryTokens}}</div>
|
||||
</div>
|
||||
<div class="wide100p flex-container flexNoGap flexFlowColumn">
|
||||
<div class="wide100p flex-container">
|
||||
<div class="flex1" style="color: cornflowerblue;">Extensions:</div>
|
||||
<div class="flex1" style="color: cornflowerblue;"><span data-i18n="Extensions:">Extensions:</span></div>
|
||||
<div class="">{{allAnchorsTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
@@ -101,23 +110,23 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Author's Note:</div>
|
||||
<div class="tokenItemizingSubclass"> {{authorsNoteStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{authorsNoteStringTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Smart Context:</div>
|
||||
<div class="tokenItemizingSubclass"> {{smartContextStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{smartContextStringTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Vector Storage (Chats):</div>
|
||||
<div class="tokenItemizingSubclass"> {{chatVectorsStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{chatVectorsStringTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container ">
|
||||
<div class=" flex1 tokenItemizingSubclass">-- Vector Storage (Data Bank):</div>
|
||||
<div class="tokenItemizingSubclass"> {{dataBankVectorsStringTokens}}</div>
|
||||
<div class="tokenItemizingSubclass">{{dataBankVectorsStringTokens}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wide100p flex-container">
|
||||
<div class="flex1" style="color: mediumpurple;">{{}} Bias:</div>
|
||||
<div class="flex1" style="color: mediumpurple;"><span>{{}}</span> <span data-i18n="Bias:">Bias:</span></div>
|
||||
<div class="">{{oaiBiasTokens}}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,11 +135,11 @@ Tokenizer: {{selectedTokenizer}}<br>
|
||||
<hr>
|
||||
<div class="wide100p flex-container flexFlowColumns">
|
||||
<div class="flex-container wide100p">
|
||||
<div class="flex1">Total Tokens in Prompt:</div>
|
||||
<div class=""> {{finalPromptTokens}}</div>
|
||||
<div class="flex1"><span data-i18n="Total Tokens in Prompt:">Total Tokens in Prompt:</span></div>
|
||||
<div class="">{{finalPromptTokens}}</div>
|
||||
</div>
|
||||
<div class="flex-container wide100p">
|
||||
<div class="flex1">Max Context (Context Size - Response Length):</div>
|
||||
<div class="flex1"><span data-i18n="Max Context">Max Context</span> <small data-i18n="(Context Size - Response Length)">(Context Size - Response Length)</small><span data-i18n=":">:</span></div>
|
||||
<div class="">{{thisPrompt_max_context}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,8 +4,17 @@
|
||||
<div id="copyPromptToClipboard" class="fa-solid fa-copy menu_button" title="Copy Prompt" data-i18n="[title]Copy Prompt"></div>
|
||||
<div id="diffPrevPrompt" class="fa-solid fa-code-compare menu_button" title="Show Prompt Differences" data-i18n="[title]Show Prompt Differences"></div>
|
||||
</h3>
|
||||
API/Model Used: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}<br>
|
||||
Tokenizer: {{selectedTokenizer}}<br>
|
||||
<div>
|
||||
<div>
|
||||
API/Model: {{mainApiFriendlyName}} {{#if apiUsed}}({{apiUsed}}){{/if}} {{#if modelUsed}}– {{modelUsed}}{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<small>Preset: {{presetName}}</small>
|
||||
<span>|</span>
|
||||
<small>Tokenizer: {{selectedTokenizer}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="tokenItemizingSubclass">
|
||||
Only the white numbers really matter. All numbers are estimates.
|
||||
Grey color items may not have been included in the context due to certain prompt format settings.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user