Merge branch 'staging' into deprecate-cc-or-instruct
| @@ -1,9 +1,20 @@ | ||
| 1 | 1 | > [!IMPORTANT] |
| 2 | 2 | > ここに掲載されている情報は、古かったり不完全であったりする可能性があります。最新の情報は英語版をご利用ください。 |
| 3 | 3 | |
| 4 | +![][cover] | |
| 5 | + | |
| 6 | +<div align="center"> | |
| 7 | + | |
| 4 | 8 | [English](readme.md) | [中文](readme-zh_cn.md) | 日本語 | [Русский](readme-ru_ru.md) |
| 5 | 9 | |
| 6 | - | |
| 10 | +[](https://github.com/SillyTavern/SillyTavern/stargazers) | |
| 11 | +[](https://github.com/SillyTavern/SillyTavern/network) | |
| 12 | +[](https://github.com/SillyTavern/SillyTavern/issues) | |
| 13 | +[](https://github.com/SillyTavern/SillyTavern/pulls) | |
| 14 | + | |
| 15 | +</div> | |
| 16 | + | |
| 17 | +--- | |
| 7 | 18 | |
| 8 | 19 | モバイルデバイスにも適したレイアウト・マルチAPI(KoboldAI/CPP、Horde、NovelAI、Ooba、OpenAI、OpenRouter、Claude、Scale)、VN ライクな Waifu モード、Stable Diffusion、TTS、WorldInfo(伝承本)、カスタマイズ可能なUI、自動翻訳、大量のプロンプトオプション+サードパーティの拡張機能をインストールする機能 |
| 9 | 20 | |
| @@ -284,8 +295,8 @@ ST サーバを再起動すると、ユーザ名とパスワードさえ知っ | ||
| 284 | 295 | |
| 285 | 296 | ## スクリーンショット |
| 286 | 297 | |
| 287 | 298 | <img width="400500" alt="image" src="https://user-images.githubusercontentgithub.com/18619528/228649245user-8061c60fattachments/assets/9b5f32f0-63dcc3b3-488e4102-9325b3f5-f151b7a3ec2d.png0e9213c0f50f"> |
| 288 | 299 | <img width="400500" alt="image" src="https://user-images.githubusercontentgithub.com/18619528/228649856user-fbdeef05attachments/assets/913fdbaa-d7277d33-4d5a42f1-be80ae2c-266cbbc6b811.png89dca41c53d1"> |
| 289 | 300 | |
| 290 | 301 | ## ライセンスとクレジット |
| 291 | 302 | |
| @@ -314,3 +325,6 @@ ST サーバを再起動すると、ユーザ名とパスワードさえ知っ | ||
| 314 | 325 | * @doloroushyeonse による韓国語翻訳 |
| 315 | 326 | * k_euler_a による Horde のサポート <https://github.com/Teashrock> |
| 316 | 327 | * [@XXpE3](https://github.com/XXpE3) による中国語翻訳、中国語 ISSUES の連絡先は @XXpE3 |
| 328 | + | |
| 329 | +<!-- LINK GROUP --> | |
| 330 | +[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44 | |
| @@ -3,9 +3,20 @@ | ||
| 3 | 3 | |
| 4 | 4 | <a name="readme-top"></a> |
| 5 | 5 | |
| 6 | +![][cover] | |
| 7 | + | |
| 8 | +<div align="center"> | |
| 9 | + | |
| 6 | 10 | [English](readme.md) | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | Русский |
| 7 | 11 | |
| 8 | -![][cover] | |
| 12 | +[](https://github.com/SillyTavern/SillyTavern/stargazers) | |
| 13 | +[](https://github.com/SillyTavern/SillyTavern/network) | |
| 14 | +[](https://github.com/SillyTavern/SillyTavern/issues) | |
| 15 | +[](https://github.com/SillyTavern/SillyTavern/pulls) | |
| 16 | + | |
| 17 | +</div> | |
| 18 | + | |
| 19 | +--- | |
| 9 | 20 | |
| 10 | 21 | Мобайл-френдли интерфейс, поддержка множества API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale), ВН-образный режим Вайфу, Stable Diffusion, TTS, поддержка миров (лорбуков), кастомизируемый UI, автоперевод, тончайшая настройка промптов + возможность устанавливать расширения. |
| 11 | 22 | |
| @@ -29,8 +40,8 @@ SillyTavern — это форк версии TavernAI 1.2.8, который ра | ||
| 29 | 40 | |
| 30 | 41 | ## Скриншоты |
| 31 | 42 | |
| 32 | 43 | <img width="400500" alt="image" src="https://github.com/SillyTavern/SillyTavernuser-attachments/assets/61471128/e902c7a29b5f32f0-45a6c3b3-44154102-97aab3f5-c59c597669c10e9213c0f50f"> |
| 33 | 44 | <img width="400500" alt="image" src="https://github.com/SillyTavern/SillyTavernuser-attachments/assets/61471128/f8a79c47913fdbaa-4fe97d33-456442f1-9e4aae2c-bf247ed1c96189dca41c53d1"> |
| 34 | 45 | |
| 35 | 46 | ### Ветки |
| 36 | 47 | |
| @@ -355,7 +366,7 @@ SillyTavern сохраняет ключи от ваших API в файле `sec | ||
| 355 | 366 | |
| 356 | 367 | <!-- LINK GROUP --> |
| 357 | 368 | [back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square |
| 358 | 369 | [cover]: https://github.com/SillyTavern/SillyTavernuser-attachments/assets/18619528/c2be4c3f01a6ae9a-aada16aa-4f6445f2-87a38bff-ae35a68b61a432b5dc587e44 |
| 359 | 370 | [discord-link]: https://discord.gg/sillytavern |
| 360 | 371 | [discord-shield]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square |
| 361 | 372 | [discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge |
| @@ -1,9 +1,20 @@ | ||
| 1 | 1 | > [!IMPORTANT] |
| 2 | 2 | > 这里的信息可能已经过时或不完整,仅供您参考。请使用英文版本获取最新信息。 |
| 3 | 3 | |
| 4 | +![][cover] | |
| 5 | + | |
| 6 | +<div align="center"> | |
| 7 | + | |
| 4 | 8 | [English](readme.md) | 中文 | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) |
| 5 | 9 | |
| 6 | - | |
| 10 | +[](https://github.com/SillyTavern/SillyTavern/stargazers) | |
| 11 | +[](https://github.com/SillyTavern/SillyTavern/network) | |
| 12 | +[](https://github.com/SillyTavern/SillyTavern/issues) | |
| 13 | +[](https://github.com/SillyTavern/SillyTavern/pulls) | |
| 14 | + | |
| 15 | +</div> | |
| 16 | + | |
| 17 | +--- | |
| 7 | 18 | |
| 8 | 19 | 移动设备界面友好,多种人工智能服务或模型支持(KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale),类似 Galgame 的 老 婆 模 式,Horde SD,文本系统语音生成,世界信息(Lorebooks),可定制的界面,自动翻译,和比你所需要的更多的 Prompt。附带扩展服务,支持文本绘画生成与语音生成和基于向量数据库 的聊天信息总结。 |
| 9 | 20 | |
| @@ -278,8 +289,8 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。 | ||
| 278 | 289 | |
| 279 | 290 | ## 屏幕截图 |
| 280 | 291 | |
| 281 | 292 | <img width="400500" alt="image" src="https://user-images.githubusercontentgithub.com/18619528/228649245user-8061c60fattachments/assets/9b5f32f0-63dcc3b3-488e4102-9325b3f5-f151b7a3ec2d.png0e9213c0f50f"> |
| 282 | 293 | <img width="400500" alt="image" src="https://user-images.githubusercontentgithub.com/18619528/228649856user-fbdeef05attachments/assets/913fdbaa-d7277d33-4d5a42f1-be80ae2c-266cbbc6b811.png89dca41c53d1"> |
| 283 | 294 | |
| 284 | 295 | ## 许可证和贡献 |
| 285 | 296 | |
| @@ -306,3 +317,6 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。 | ||
| 306 | 317 | * Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc |
| 307 | 318 | * Korean translation by @doloroushyeonse |
| 308 | 319 | * 中文翻译由 [@XXpE3](https://github.com/XXpE3) 完成,中文 ISSUES 可以联系 @XXpE3 |
| 320 | + | |
| 321 | +<!-- LINK GROUP --> | |
| 322 | +[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44 | |
| @@ -1,76 +1,103 @@ | ||
| 1 | 1 | <a name="readme-top"></a> |
| 2 | 2 | |
| 3 | -English | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | |
| 4 | - | |
| 5 | 3 | ![][cover] |
| 6 | 4 | |
| 7 | -Mobile-friendly layout, Multi-API (KoboldAI/CPP, Horde, NovelAI, Ooba, OpenAI, OpenRouter, Claude, Scale), VN-like Waifu Mode, Stable Diffusion, TTS, WorldInfo (lorebooks), customizable UI, auto-translate, and more prompt options than you'd ever want or need + ability to install third-party extensions. | |
| 5 | +<div align="center"> | |
| 8 | 6 | |
| 9 | -Based on a fork of [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8 | |
| 7 | +English | [中文](readme-zh_cn.md) | [日本語](readme-ja_jp.md) | [Русский](readme-ru_ru.md) | |
| 10 | 8 | |
| 11 | -## Important news! | |
| 9 | +[](https://github.com/SillyTavern/SillyTavern/stargazers) | |
| 10 | +[](https://github.com/SillyTavern/SillyTavern/network) | |
| 11 | +[](https://github.com/SillyTavern/SillyTavern/issues) | |
| 12 | +[](https://github.com/SillyTavern/SillyTavern/pulls) | |
| 12 | 13 | |
| 13 | -1. We have created a [Documentation website](https://docs.sillytavern.app/) to answer most of your questions and help you get started. | |
| 14 | +</div> | |
| 14 | 15 | |
| 15 | -2. Missing extensions after the update? Since the 1.10.6 release version, most of the previously built-in extensions have been converted to downloadable add-ons. You can download them via the built-in "Download Extensions and Assets" menu in the extensions panel (stacked blocks icon in the top bar). | |
| 16 | +--- | |
| 16 | 17 | |
| 17 | -3. Unsupported platform: android arm LEtime-web. 32-bit Android requires an external dependency that can't be installed with npm. Use the following command to install it: `pkg install esbuild`. Then run the usual installation steps. | |
| 18 | +SillyTavern provides a single unified interface for many LLM APIs (KoboldAI/CPP, Horde, NovelAI, Ooba, Tabby, OpenAI, OpenRouter, Claude, Mistral and more), a mobile-friendly layout, Visual Novel Mode, Automatic1111 & ComfyUI API image generation integration, TTS, WorldInfo (lorebooks), customizable UI, auto-translate, more prompt options than you'd ever want or need, and endless growth potential via third-party extensions. | |
| 18 | 19 | |
| 19 | -### Brought to you by Cohee, RossAscends, and the SillyTavern community | |
| 20 | +We have a [Documentation website](https://docs.sillytavern.app/) to answer most of your questions and help you get started. | |
| 20 | 21 | |
| 21 | 22 | ### What is SillyTavern or TavernAI? |
| 22 | 23 | |
| 23 | 24 | SillyTavern is a user interface(or youST canfor installshort) onis youra computerlocally (andinstalled Androiduser phones)interface that allows you to interact with text generation AIs and chat/roleplayLLMs, withimage charactersgeneration youengines, orand theTTS communityvoice createmodels. |
| 24 | 25 | |
| 25 | 26 | SillyTavernBeginning isin February 2023 as a fork of TavernAI 1.2.8, whichSillyTavern isnow underhas moreover active100 developmentcontributors and has2 addedyears manyof majorindependent features.development Atunder thisits pointbelt, they canand becontinues thoughtto ofserve as completelya independentleading programssoftware for savvy AI hobbyists. |
| 26 | 27 | |
| 27 | 28 | ## ScreenshotsOur Vision |
| 28 | 29 | |
| 29 | -<img width="400" alt="image" src="https://github.com/SillyTavern/SillyTavern/assets/61471128/e902c7a2-45a6-4415-97aa-c59c597669c1"> | |
| 30 | +1. We aim to empower users with as much utility and control over their LLM prompts as possible. The steep learning curve is part of the fun! | |
| 30 | -<img width="400" alt="image" src="https://github.com/SillyTavern/SillyTavern/assets/61471128/f8a79c47-4fe9-4564-9e4a-bf247ed1c961"> | |
| 31 | +2. We do not provide any online or hosted services, nor programmatically track any user data. | |
| 32 | +3. SillyTavern is a passion project brought to you by a dedicated community of LLM enthusiasts, and will always be free and open sourced. | |
| 31 | 33 | |
| 32 | 34 | ### Branches |
| 33 | 35 | |
| 34 | 36 | SillyTavern is being developed using a two-branch system to ensure a smooth experience for all users. |
| 35 | 37 | |
| 36 | 38 | * `release` -🌟 **Recommended for most users.** This is the most stable and recommended branch, updated only when major releases are pushed. It's suitable for the majority of users. Typically updated once a month. |
| 37 | 39 | * `staging` - ⚠️ **Not recommended for casual use.** This branch has the latest features, but be cautious as it may break at any time. Only for power users and enthusiasts. Updates several times daily. |
| 38 | 40 | |
| 39 | 41 | If you're not familiar with using the git CLI or don't understand what a branch is, don't worry! The release branch is always the preferable option for you. |
| 40 | 42 | |
| 41 | 43 | ### What do I need other than SillyTavern? |
| 42 | 44 | |
| 43 | 45 | On its ownSince SillyTavern is useless, as it's just aonly useran interface. You, haveyou towill haveneed access to an AI systemLLM backend thatto provide inference. You can actuse asAI theHorde roleplayfor characterinstant out-of-the-box chatting. ThereAside arefrom variousthat, supportedwe backends:support OpenAPImany APIother (GPT),local KoboldAIand (eithercloud-based runningLLM locallybackends: orOpenAI-compatible onAPI, GoogleKoboldAI, Colab)Tabby, and many more. You can read more about thisour supported APIs in [the FAQ](https://docs.sillytavern.app/usage/faqapi-connections/). |
| 44 | 46 | |
| 45 | 47 | ### Do I need a powerful PC to run SillyTavern? |
| 46 | 48 | |
| 47 | -Since SillyTavern is only a user interface, it has tiny hardware requirements, it will run on anything. It's the AI system backend that needs to be powerful. | |
| 49 | +The hardware requirements are minimal: it will run on anything that can run NodeJS 18 or higher. If you intend to do LLM inference on your local machine, we recommend a 3000-series NVIDIA graphics card with at least 6GB of VRAM. Check your backend's documentation for more details. | |
| 50 | + | |
| 51 | +### Suggested Backends (not affiliated) | |
| 52 | + | |
| 53 | +* [AI Horde](https://aihorde.net/) - use models hosted by volunteers. Requires no further setup | |
| 54 | +* [KoboldCpp](https://github.com/LostRuins/koboldcpp) - a community's favorite for running GGUF models locally | |
| 55 | +* [tabbyAPI](https://github.com/theroyallab/tabbyAPI) - a popular, lightweight, locally-hosted exl2 inference API | |
| 56 | +* [OpenRouter](https://openrouter.ai) - a single API for many cloud providers (OpenAI, Claude, Meta Llama, etc.) as well as popular community models. | |
| 48 | 57 | |
| 49 | 58 | ## Questions or suggestions? |
| 50 | 59 | |
| 51 | 60 | ### We now have a community Discord server |
| 52 | 61 | |
| 53 | 62 | | [![][discord-shield-badge]][discord-link] | [Join our Discord community!](https://discord.gg/sillytavern) Get support, share favorite characters and prompts. | |
| 54 | 63 | | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | |
| 55 | 64 | |
| 56 | 65 | Or get in touch with the developers directly: |
| 57 | 66 | |
| 58 | 67 | * Discord: cohee or, rossascends, wolfsblvt |
| 59 | 68 | * Reddit: [/u/RossAscends](https://www.reddit.com/user/RossAscends/) or, [/u/sillylossy](https://www.reddit.com/user/sillylossy/), [u/Wolfsblvt](https://www.reddit.com/user/Wolfsblvt/) |
| 60 | 69 | * [Post a GitHub issue](https://github.com/SillyTavern/SillyTavern/issues) |
| 61 | 70 | |
| 62 | -## This version includes | |
| 71 | +### I like your project! How do I contribute? | |
| 72 | + | |
| 73 | +1. Send pull requests. Learn how to contribute: [CONTRIBUTING.md](../CONTRIBUTING.md) | |
| 74 | +2. Send feature suggestions and issue reports using the provided templates. | |
| 75 | +3. Read this entire readme file and check the documentation website first, to avoid sending duplicate issues. | |
| 76 | + | |
| 77 | +## Screenshots | |
| 78 | + | |
| 79 | +<img width="500" alt="image" src="https://github.com/user-attachments/assets/9b5f32f0-c3b3-4102-b3f5-0e9213c0f50f"> | |
| 80 | +<img width="500" alt="image" src="https://github.com/user-attachments/assets/913fdbaa-7d33-42f1-ae2c-89dca41c53d1"> | |
| 81 | + | |
| 82 | +## Character Cards | |
| 83 | + | |
| 84 | +SillyTavern is built around the concept of "character cards". A character card is a collection of prompts that set the behavior of the LLM and is required to have persistent conversations in SillyTavern. They function similarly to ChatGPT's GPTs or Poe's bots. The content of a character card can be anything: an abstract scenario, an assistant tailored for a specific task, a famous personality or a fictional character. | |
| 85 | + | |
| 86 | +The name field is the only required character card input. To start a neutral conversation with the language model, create a new card simply called "Assistant" and leave the rest of the boxes blank. For a more themed chat, you can provide the language model with various background details, behavior and writing patterns, and a scenario to jump start the chat. | |
| 63 | 87 | |
| 64 | -* A heavily modified TavernAI 1.2.8 (more than 50% of code rewritten or optimized) | |
| 88 | +To have a quick conversation without selecting a character card or to just test the LLM connection, simply type your prompt input into the input bar on the Welcome Screen after opening SillyTavern. Please note that such chats are temporary and will not be saved. | |
| 65 | -* Swipes | |
| 89 | + | |
| 66 | -* Group chats: multi-bot rooms for characters to talk to you or each other | |
| 90 | +To get a general idea on how to define character cards, see the default character (Seraphina) or download selected community-made cards from the "Download Extensions & Assets" menu. | |
| 67 | -* Chat checkpoints / branching | |
| 91 | + | |
| 68 | -* Advanced KoboldAI / TextGen generation settings with a lot of community-made presets | |
| 92 | +## Key Features | |
| 93 | + | |
| 94 | +* Advanced text generation settings with many community-made presets | |
| 69 | 95 | * World Info support: create rich lore or save tokens on your character card |
| 70 | -* [OpenRouter](https://openrouter.ai) connection for various APIs (Claude, GPT-4/3.5 and more) | |
| 96 | +* Group chats: multi-bot rooms for characters to talk to you and/or each other | |
| 71 | -* [Oobabooga's TextGen WebUI](https://github.com/oobabooga/text-generation-webui) API connection | |
| 97 | +* Rich UI customization options: theme colors, background images, custom CSS, and more | |
| 72 | -* [AI Horde](https://aihorde.net/) connection | |
| 98 | +* User personas: let the AI know a bit about you for greater immersion | |
| 73 | -* Prompt generation formatting tweaking | |
| 99 | +* Built-in RAG support: add documents to your chats for the AI to reference | |
| 100 | +* Extensive chat commands subsystem and own [scripting engine](https://docs.sillytavern.app/usage/st-script/) | |
| 74 | 101 | |
| 75 | 102 | ## Extensions |
| 76 | 103 | |
| @@ -78,58 +105,26 @@ SillyTavern has extensibility support. | ||
| 78 | 105 | |
| 79 | 106 | * Character emotional expressions (sprites) |
| 80 | 107 | * Auto-Summary of the chat history |
| 81 | -* Sending images to chat, and the AI interpreting the content | |
| 108 | +* Automatic UI and chat translation | |
| 82 | -* Stable Diffusion image generation (5 chat-related presets plus 'free mode') | |
| 109 | +* Stable Diffusion/FLUX/DALL-E image generation | |
| 83 | 110 | * Text-to-speech for AI response messages (via ElevenLabs, Silero, or the OS's System TTS) |
| 84 | -* Many more available to download from the "Download Extensions & Assets" menu. | |
| 111 | +* Web Search capabilities for adding additional real world context to your prompts | |
| 112 | +* Many more are available to download from the "Download Extensions & Assets" menu. | |
| 85 | 113 | |
| 86 | 114 | Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern.app/). |
| 87 | 115 | |
| 88 | -## UI/CSS/Quality of Life tweaks by RossAscends | |
| 89 | - | |
| 90 | -* Mobile UI optimized for iOS, and supports saving a shortcut to the home screen and opening in fullscreen mode. | |
| 91 | -* HotKeys | |
| 92 | - * Up = Edit last message in chat | |
| 93 | - * Ctrl+Up = Edit last USER message in chat | |
| 94 | - * Left = swipe left | |
| 95 | - * Right = swipe right (NOTE: swipe hotkeys are disabled when the chat bar has something typed into it) | |
| 96 | - * Ctrl+Left = view locally stored variables (in the browser console window) | |
| 97 | - * Enter (with chat bar selected) = send your message to AI | |
| 98 | - * Ctrl+Enter = Regenerate the last AI response | |
| 99 | - | |
| 100 | -* User Name Changes and Character Deletion no longer force the page to refresh. | |
| 101 | - | |
| 102 | -* Toggle option to automatically connect to API on page load. | |
| 103 | -* Toggle option to automatically load the most recently viewed character on page load. | |
| 104 | -* Better Token Counter - works on unsaved characters, and shows both permanent and temporary tokens. | |
| 105 | - | |
| 106 | -* Better Past Chats View | |
| 107 | - * New Chat filenames are saved in a readable format of "(character) - (when it was created)" | |
| 108 | - * Chat preview increased from 40 characters to 300. | |
| 109 | - * Multiple options for characters list sorting (by name, creation date, chat sizes). | |
| 110 | - | |
| 111 | -* By default the left and right settings panel will close when you click away from it. | |
| 112 | -* Clicking the Lock on the nav panel will hold the panel open, and this setting be remembered across sessions. | |
| 113 | -* Nav panel status of open or closed will also be saved across sessions. | |
| 114 | - | |
| 115 | -* Customizable chat UI: | |
| 116 | - * Play a sound when a new message arrives | |
| 117 | - * Switch between round or rectangle avatar styles | |
| 118 | - * Have a wider chat window on the desktop | |
| 119 | - * Optional semi-transparent glass-like panels | |
| 120 | - * Customizable page colors for 'main text', 'quoted text', and 'italics text'. | |
| 121 | - * Customizable UI background color and blur amount | |
| 122 | - | |
| 123 | 116 | # ⌛ Installation |
| 124 | 117 | |
| 125 | 118 | > \[!WARNING] |
| 119 | +> | |
| 126 | 120 | > * DO NOT INSTALL INTO ANY WINDOWS CONTROLLED FOLDER (Program Files, System32, etc). |
| 127 | 121 | > * DO NOT RUN START.BAT WITH ADMIN PERMISSIONS |
| 128 | 122 | > * INSTALLATION ON WINDOWS 7 IS IMPOSSIBLE AS IT CAN NOT RUN NODEJS 18.16 |
| 129 | 123 | |
| 130 | 124 | ## 🪟 Windows |
| 131 | 125 | |
| 132 | 126 | ### Installing via Git |
| 127 | + | |
| 133 | 128 | 1. Install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended) |
| 134 | 129 | 2. Install [Git for Windows](https://gitforwindows.org/) |
| 135 | 130 | 3. Open Windows Explorer (`Win+E`) |
| @@ -137,24 +132,16 @@ Tutorials on how to use them can be found in the [Docs](https://docs.sillytavern | ||
| 137 | 132 | 5. Open a Command Prompt inside that folder by clicking in the 'Address Bar' at the top, typing `cmd`, and pressing Enter. |
| 138 | 133 | 6. Once the black box (Command Prompt) pops up, type ONE of the following into it and press Enter: |
| 139 | 134 | |
| 140 | 135 | -* for Release Branch: `git clone https://github.com/SillyTavern/SillyTavern -b release` |
| 141 | 136 | -* for Staging Branch: `git clone https://github.com/SillyTavern/SillyTavern -b staging` |
| 142 | 137 | |
| 143 | 138 | 7. Once everything is cloned, double-click `Start.bat` to make NodeJS install its requirements. |
| 144 | 139 | 8. The server will then start, and SillyTavern will pop up in your browser. |
| 145 | 140 | |
| 146 | 141 | ### Installing via SillyTavernGitHub LauncherDesktop |
| 147 | -1. On your keyboard: press **`WINDOWS + R`** to open Run dialog box. Then, run the following command to install git: | |
| 148 | -```shell | |
| 149 | -cmd /c winget install -e --id Git.Git | |
| 150 | -``` | |
| 151 | -2. On your keyboard: press **`WINDOWS + E`** to open File Explorer, then navigate to the folder where you want to install the launcher. Once in the desired folder, type `cmd` into the address bar and press enter. Then, run the following command: | |
| 152 | -```shell | |
| 153 | -git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat | |
| 154 | -``` | |
| 155 | 142 | |
| 156 | -## Installing via GitHub Desktop | |
| 157 | 143 | (This allows git usage **only** in GitHub Desktop, if you want to use `git` on the command line too, you also need to install [Git for Windows](https://gitforwindows.org/)) |
| 144 | + | |
| 158 | 145 | 1. Install [NodeJS](https://nodejs.org/en) (latest LTS version is recommended) |
| 159 | 146 | 2. Install [GitHub Desktop](https://central.github.com/deployments/desktop/desktop/latest/win32) |
| 160 | 147 | 3. After installing GitHub Desktop, click on `Clone a repository from the internet....` (Note: You **do NOT need** to create a GitHub account for this step) |
| @@ -172,50 +159,82 @@ For MacOS / Linux all of these will be done in a Terminal. | ||
| 172 | 159 | 1. Install git and nodeJS (the method for doing this will vary depending on your OS) |
| 173 | 160 | 2. Clone the repo |
| 174 | 161 | |
| 175 | 162 | -* for Release Branch: `git clone https://github.com/SillyTavern/SillyTavern -b release` |
| 176 | 163 | -* for Staging Branch: `git clone https://github.com/SillyTavern/SillyTavern -b staging` |
| 177 | 164 | |
| 178 | 165 | 3. `cd SillyTavern` to navigate into the install folder. |
| 179 | 166 | 4. Run the `start.sh` script with one of these commands: |
| 180 | 167 | |
| 181 | 168 | -* `./start.sh` |
| 182 | 169 | -* `bash start.sh` |
| 170 | + | |
| 171 | +## ⚡ Installing via SillyTavern Launcher | |
| 172 | + | |
| 173 | +SillyTavern Launcher is an installation wizard that will help you get setup with many options, including installing a backend for local inference. | |
| 174 | + | |
| 175 | +### For Windows users | |
| 183 | 176 | |
| 184 | -## Installing via SillyTavern Launcher | |
| 177 | +1. On your keyboard: press **`WINDOWS + R`** to open Run dialog box. Then, run the following command to install git: | |
| 178 | + | |
| 179 | +```shell | |
| 180 | +cmd /c winget install -e --id Git.Git | |
| 181 | +``` | |
| 182 | + | |
| 183 | +2. On your keyboard: press **`WINDOWS + E`** to open File Explorer, then navigate to the folder where you want to install the launcher. Once in the desired folder, type `cmd` into the address bar and press enter. Then, run the following command: | |
| 184 | + | |
| 185 | +```shell | |
| 186 | +git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher && start installer.bat | |
| 187 | +``` | |
| 185 | 188 | |
| 186 | 189 | ### For Linux users |
| 190 | + | |
| 187 | 191 | 1. Open your favorite terminal and install git |
| 188 | 192 | 2. Git clone the Sillytavern-Launcher with: |
| 193 | + | |
| 189 | 194 | ```shell |
| 190 | 195 | git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher |
| 191 | 196 | ``` |
| 197 | + | |
| 192 | 198 | 3. Start the installer.sh with: |
| 199 | + | |
| 193 | 200 | ```shell |
| 194 | 201 | chmod +x install.sh && ./install.sh |
| 195 | 202 | ``` |
| 203 | + | |
| 196 | 204 | 4. After installation start the launcher.sh with: |
| 205 | + | |
| 197 | 206 | ```shell |
| 198 | 207 | chmod +x launcher.sh && ./launcher.sh |
| 199 | 208 | ``` |
| 200 | 209 | |
| 201 | 210 | ### For Mac users |
| 211 | + | |
| 202 | 212 | 1. Open a terminal and install brew with: |
| 213 | + | |
| 203 | 214 | ```shell |
| 204 | 215 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
| 205 | 216 | ``` |
| 217 | + | |
| 206 | 218 | 2. Install git with: |
| 219 | + | |
| 207 | 220 | ```shell |
| 208 | 221 | brew install git |
| 209 | 222 | ``` |
| 223 | + | |
| 210 | 224 | 3. Git clone the Sillytavern-Launcher with: |
| 225 | + | |
| 211 | 226 | ```shell |
| 212 | 227 | git clone https://github.com/SillyTavern/SillyTavern-Launcher.git && cd SillyTavern-Launcher |
| 213 | 228 | ``` |
| 229 | + | |
| 214 | 230 | 4. Start the installer.sh with: |
| 231 | + | |
| 215 | 232 | ```shell |
| 216 | 233 | chmod +x install.sh && ./install.sh |
| 217 | 234 | ``` |
| 235 | + | |
| 218 | 236 | 5. After installation start the launcher.sh with: |
| 237 | + | |
| 219 | 238 | ```shell |
| 220 | 239 | chmod +x launcher.sh && ./launcher.sh |
| 221 | 240 | ``` |
| @@ -236,18 +255,18 @@ You will need two mandatory directory mappings and a port mapping to allow Silly | ||
| 236 | 255 | |
| 237 | 256 | ##### Volume Mappings |
| 238 | 257 | |
| 239 | 258 | -* [config] - The directory where SillyTavern configuration files will be stored on your host machine |
| 240 | 259 | -* [data] - The directory where SillyTavern user data (including characters) will be stored on your host machine |
| 241 | 260 | -* [plugins] - (optional) The directory where SillyTavern server plugins will be stored on your host machine |
| 242 | 261 | |
| 243 | 262 | ##### Port Mappings |
| 244 | 263 | |
| 245 | 264 | -* [PublicPort] - The port to expose the traffic on. This is mandatory, as you will be accessing the instance from outside of its virtual machine container. DO NOT expose this to the internet without implementing a separate service for security. |
| 246 | 265 | |
| 247 | 266 | ##### Additional Settings |
| 248 | 267 | |
| 249 | 268 | -* [DockerNet] - The docker network that the container should be created with a connection to. If you don't know what it is, see the [official Docker documentation](https://docs.docker.com/reference/cli/docker/network/). |
| 250 | 269 | -* [version] - On the right-hand side of this GitHub page, you'll see "Packages". Select the "sillytavern" package and you'll see the image versions. The image tag "latest" will keep you up-to-date with the current release. You can also utilize "staging" and "release" tags that point to the nightly images of the respective branches, but this may not be appropriate, if you are utilizing extensions that could be broken, and may need time to update. |
| 251 | 270 | |
| 252 | 271 | #### Install command |
| 253 | 272 | |
| @@ -258,23 +277,28 @@ You will need two mandatory directory mappings and a port mapping to allow Silly | ||
| 258 | 277 | |
| 259 | 278 | > Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config. |
| 260 | 279 | |
| 261 | 280 | ## 📱 Mobile - Installing via termuxTermux on Android OS |
| 262 | 281 | |
| 263 | 282 | > \[!NOTE] |
| 264 | 283 | > **SillyTavern can be run natively on Android phonesdevices using Termux., Pleasebut referwe todo thisnot guideprovide byofficial ArroganceComplex#2659:support for this use case.** |
| 284 | +> | |
| 285 | +> **Please refer to this guide by ArroganceComplex#2659:** | |
| 286 | +> | |
| 265 | 287 | > * <https://rentry.org/STAI-Termux> |
| 266 | 288 | |
| 289 | +**Unsupported platform: android arm LEtime-web.** 32-bit Android requires an external dependency that can't be installed with npm. Use the following command to install it: `pkg install esbuild`. Then run the usual installation steps. | |
| 267 | 290 | |
| 268 | 291 | ## API keys management |
| 269 | 292 | |
| 270 | 293 | SillyTavern saves your API keys to a `secrets.json` file in the user data directory (`/data/default-user/secrets.json` is the default path). |
| 271 | 294 | |
| 272 | 295 | By default, theyAPI keys will not be exposedvisible tofrom athe frontendinterface after you enterhave saved them and reloadrefreshed the page. |
| 273 | 296 | |
| 274 | 297 | In order to enable viewing your keys by clicking a button in the API block: |
| 275 | 298 | |
| 276 | 299 | 1. Set the value of `allowKeysExposure` to `true` in `config.yaml` file. |
| 277 | 300 | 2. Restart the SillyTavern server. |
| 301 | +3. Click the 'View hidden API keys' link at the bottom right of the API Connection Panel. | |
| 278 | 302 | |
| 279 | 303 | ## Command-line arguments |
| 280 | 304 | |
| @@ -292,134 +316,43 @@ Start.bat --port 8000 --listen false | ||
| 292 | 316 | |
| 293 | 317 | ### Supported arguments |
| 294 | 318 | |
| 295 | 319 | | Option | Description | Type | Default | |
| 296 | 320 | |-------------------------|------------------------------------------------------------------------------------------------------|----------|------------------------------| |
| 297 | 321 | | `--version` | Show version number | boolean | | |
| 298 | 322 | | `--enableIPv6` | Enables IPv6. | boolean | false | |
| 299 | 323 | | `--enableIPv4` | Enables IPv4. | boolean | true | |
| 300 | 324 | | `--port` | Sets the port under which SillyTavern will run. If not provided falls back to yaml config 'port'. | number | 8000 | |
| 301 | 325 | | `--dnsPreferIPv6` | Prefers IPv6 for dns. If not provided falls back to yaml config 'preferIPv6'. | boolean | false | |
| 302 | 326 | | `--autorun` | Automatically launch SillyTavern in the browser. If not provided falls back to yaml config 'autorun'.| boolean | false | |
| 303 | 327 | | `--autorunHostname` | The autorun hostname, probably best left on 'auto'. | string | null | |
| 304 | 328 | | `--autorunPortOverride` | Overrides the port for autorun. | string | null | |
| 305 | 329 | | `--listen` | SillyTavern is listening on all network interfaces. If not provided falls back to yaml config 'listen'.| boolean | false | |
| 306 | 330 | | `--corsProxy` | Enables CORS proxy. If not provided falls back to yaml config 'enableCorsProxy'. | boolean | false | |
| 307 | 331 | | `--disableCsrf` | Disables CSRF protection | boolean | null | |
| 308 | 332 | | `--ssl` | Enables SSL | boolean | false | |
| 309 | 333 | | `--certPath` | Path to your certificate file. | string | "certs/cert.pem" | |
| 310 | 334 | | `--keyPath` | Path to your private key file. | string | "certs/privkey.pem" | |
| 311 | 335 | | `--whitelist` | Enables whitelist mode | boolean | null | |
| 312 | 336 | | `--dataRoot` | Root directory for data storage | string | null | |
| 313 | 337 | | `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode. | boolean | null | |
| 314 | 338 | | `--basicAuthMode` | Enables basic authentication | boolean | null | |
| 315 | 339 | | `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean | null | |
| 316 | 340 | | `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string | null | |
| 317 | 341 | | `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array | null | |
| 318 | 342 | |
| 319 | 343 | ## Remote connections |
| 320 | 344 | |
| 321 | 345 | Most often this is for people who want to use SillyTavern on their mobile phones while their PC runs the ST server on the same wifiWi-Fi network. However, it can be used to allow remote connections from anywhere as well. |
| 322 | - | |
| 323 | -However, it can be used to allow remote connections from anywhere as well. | |
| 324 | - | |
| 325 | -**IMPORTANT: Refer to the official guide if you want to configure SillyTavern user accounts with (optional) password protection: [Users](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users).** | |
| 326 | - | |
| 327 | -### 1. Managing whitelisted IPs | |
| 328 | - | |
| 329 | -* Create a new text file inside your SillyTavern base install folder called `whitelist.txt`. | |
| 330 | -* Open the file in a text editor, and add a list of IPs you want to be allowed to connect. | |
| 331 | - | |
| 332 | -*Both individual IPs and wildcard IP ranges are accepted. Examples:* | |
| 333 | - | |
| 334 | -```txt | |
| 335 | -192.168.0.1 | |
| 336 | -192.168.0.20 | |
| 337 | -``` | |
| 338 | - | |
| 339 | -or | |
| 340 | - | |
| 341 | -```txt | |
| 342 | -192.168.0.* | |
| 343 | -``` | |
| 344 | - | |
| 345 | -(the above wildcard IP range will allow any device on the local network to connect) | |
| 346 | - | |
| 347 | -CIDR masks are also accepted (eg. 10.0.0.0/24). | |
| 348 | - | |
| 349 | -* Save the `whitelist.txt` file. | |
| 350 | -* Restart your ST server. | |
| 351 | - | |
| 352 | -Now devices which have the IP specified in the file will be able to connect. | |
| 353 | - | |
| 354 | -*Note: `config.yaml` also has a `whitelist` array, which you can use in the same way, but this array will be ignored if `whitelist.txt` exists.* | |
| 355 | - | |
| 356 | -### 2. Getting the IP for the ST host machine | |
| 357 | - | |
| 358 | -After the whitelist has been setup, you'll need the IP of the ST-hosting device. | |
| 359 | - | |
| 360 | -If the ST-hosting device is on the same wifi network, you will use the ST-host's internal wifi IP: | |
| 361 | - | |
| 362 | -* For Windows: windows button > type `cmd.exe` in the search bar > type `ipconfig` in the console, hit Enter > look for `IPv4` listing. | |
| 363 | - | |
| 364 | -If you (or someone else) want to connect to your hosted ST while not being on the same network, you will need the public IP of your ST-hosting device. | |
| 365 | - | |
| 366 | -* While using the ST-hosting device, access [this page](https://whatismyipaddress.com/) and look for `IPv4`. This is what you would use to connect from the remote device. | |
| 367 | 346 | |
| 368 | -### 3. Connect the remote device to the ST host machine | |
| 347 | +Read the detailed guide on how to set up remote connections in the [Docs](https://docs.sillytavern.app/usage/remoteconnections/). | |
| 369 | 348 | |
| 370 | -Whatever IP you ended up with for your situation, you will put that IP address and port number into the remote device's web browser. | |
| 349 | +You may also want to configure SillyTavern user profiles with (optional) password protection: [Users](https://docs.sillytavern.app/installation/st-1.12.0-migration-guide/#users). | |
| 371 | - | |
| 372 | -A typical address for an ST host on the same wifi network would look like this: | |
| 373 | - | |
| 374 | -`http://192.168.0.5:8000` | |
| 375 | - | |
| 376 | -Use http:// NOT https:// | |
| 377 | - | |
| 378 | -### Opening your ST to all IPs | |
| 379 | - | |
| 380 | -We do not recommend doing this, but you can open `config.yaml` and change `whitelistMode` to `false`. | |
| 381 | - | |
| 382 | -You must remove (or rename) `whitelist.txt` in the SillyTavern base install folder if it exists. | |
| 383 | - | |
| 384 | -This is usually an insecure practice, so we require you to set a username and password when you do this. | |
| 385 | - | |
| 386 | -The username and password are set in `config.yaml`. | |
| 387 | - | |
| 388 | -After restarting your ST server, any device will be able to connect to it, regardless of their IP as long as they know the username and password. | |
| 389 | - | |
| 390 | -### Still Unable To Connect? | |
| 391 | - | |
| 392 | -* Create an inbound/outbound firewall rule for the port found in `config.yaml`. Do NOT mistake this for port-forwarding on your router, otherwise, someone could find your chat logs and that's a big no-no. | |
| 393 | -* Enable the Private Network profile type in Settings > Network and Internet > Ethernet. This is VERY important for Windows 11, otherwise, you would be unable to connect even with the aforementioned firewall rules. | |
| 394 | 350 | |
| 395 | 351 | ## Performance issues? |
| 396 | 352 | |
| 397 | 353 | Try1. enablingDisable the No Blur Effect (Fastand UI)enable modeReduced Motion on the User settingsSettings panel (UI Theme toggles category). |
| 398 | - | |
| 354 | +2. If using response streaming, set the streaming FPS to a lower value (10-15 FPS is recommended). | |
| 399 | -## I like your project! How do I contribute? | |
| 355 | +3. Make sure the browser is enabled to use GPU acceleration for rendering. | |
| 400 | - | |
| 401 | -### DO's | |
| 402 | - | |
| 403 | -1. Send pull requests | |
| 404 | -2. Send feature suggestions and issue reports using established templates | |
| 405 | -3. Read the readme file and built-in documentation before asking anything | |
| 406 | - | |
| 407 | -### DONT's | |
| 408 | - | |
| 409 | -1. Offer monetary donations | |
| 410 | -2. Send bug reports without providing any context | |
| 411 | -3. Ask the questions that were already answered numerous times | |
| 412 | - | |
| 413 | -## Where can I find the old backgrounds? | |
| 414 | - | |
| 415 | -We're moving to a 100% original content only policy, so old background images have been removed from this repository. | |
| 416 | - | |
| 417 | -You can find them archived here: | |
| 418 | - | |
| 419 | -<https://files.catbox.moe/1xevnc.zip> | |
| 420 | - | |
| 421 | - | |
| 422 | - | |
| 423 | 356 | |
| 424 | 357 | ## License and credits |
| 425 | 358 | |
| @@ -428,32 +361,19 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 428 | 361 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 429 | 362 | GNU Affero General Public License for more details.** |
| 430 | 363 | |
| 431 | -* TAI Base by Humi: MIT | |
| 364 | +* [TavernAI](https://github.com/TavernAI/TavernAI) 1.2.8 by Humi: MIT License | |
| 432 | -* Cohee's modifications and derived code: AGPL v3 | |
| 365 | +* Portions of CncAnon's TavernAITurbo mod used with permission | |
| 433 | -* RossAscends' additions: AGPL v3 | |
| 366 | +* Visual Novel Mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>) | |
| 434 | -* Portions of CncAnon's TavernAITurbo mod: Unknown license | |
| 435 | -* kingbri's various commits and suggestions (<https://github.com/bdashore3>) | |
| 436 | -* city_unit's extensions and various QoL features (<https://github.com/city-unit>) | |
| 437 | -* StefanDanielSchwarz's various commits and bug reports (<https://github.com/StefanDanielSchwarz>) | |
| 438 | -* Waifu mode inspired by the work of PepperTaco (<https://github.com/peppertaco/Tavern/>) | |
| 439 | -* Thanks Pygmalion University for being awesome testers and suggesting cool features! | |
| 440 | -* Thanks oobabooga for compiling presets for TextGen | |
| 441 | -* KoboldAI Presets from KAI Lite: <https://lite.koboldai.net/> | |
| 442 | 367 | * Noto Sans font by Google (OFL license) |
| 443 | 368 | * Icon theme by Font Awesome <https://fontawesome.com> (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) |
| 444 | -* AI Horde client library by ZeldaFan0225: <https://github.com/ZeldaFan0225/ai_horde> | |
| 369 | +* Default content by @OtisAlejandro (Seraphina character and lorebook) and @kallmeflocc (10K Discord Users Celebratory Background) | |
| 445 | -* Linux startup script by AlpinDale | |
| 446 | -* Thanks paniphons for providing a FAQ document | |
| 447 | -* 10K Discord Users Celebratory Background by @kallmeflocc | |
| 448 | -* Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc | |
| 449 | -* Korean translation by @doloroushyeonse | |
| 450 | -* k_euler_a support for Horde by <https://github.com/Teashrock> | |
| 451 | -* Chinese translation by [@XXpE3](https://github.com/XXpE3), 中文 ISSUES 可以联系 @XXpE3 | |
| 452 | 370 | * Docker guide by [@mrguymiah](https://github.com/mrguymiah) and [@Bronya-Rand](https://github.com/Bronya-Rand) |
| 453 | 371 | |
| 372 | +## Top Contributors | |
| 373 | + | |
| 374 | +[](https://github.com/SillyTavern/SillyTavern/graphs/contributors) | |
| 375 | + | |
| 454 | 376 | <!-- LINK GROUP --> |
| 455 | -[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square | |
| 377 | +[cover]: https://github.com/user-attachments/assets/01a6ae9a-16aa-45f2-8bff-32b5dc587e44 | |
| 456 | -[cover]: https://github.com/SillyTavern/SillyTavern/assets/18619528/c2be4c3f-aada-4f64-87a3-ae35a68b61a4 | |
| 457 | 378 | [discord-link]: https://discord.gg/sillytavern |
| 458 | -[discord-shield]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square | |
| 459 | 379 | [discord-shield-badge]: https://img.shields.io/discord/1100685673633153084?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=for-the-badge |
| @@ -19,7 +19,7 @@ ENV NODE_ENV=production | ||
| 19 | 19 | COPY package*.json post-install.js ./ |
| 20 | 20 | RUN \ |
| 21 | 21 | echo "*** Install npm packages ***" && \ |
| 22 | 22 | npm i --no-audit --no-fund --quietloglevel=error --no-progress --omit=dev && npm cache clean --force |
| 23 | 23 | |
| 24 | 24 | # Bundle app source |
| 25 | 25 | COPY . ./ |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | @echo off |
| 2 | 2 | pushd %~dp0 |
| 3 | 3 | set NODE_ENV=production |
| 4 | 4 | call npm install --no-audit --no-fund --quietloglevel=error --no-progress --omit=dev |
| 5 | 5 | node server.js %* |
| 6 | 6 | pause |
| 7 | 7 | popd |
| @@ -12,7 +12,7 @@ if %errorlevel% neq 0 ( | ||
| 12 | 12 | ) |
| 13 | 13 | ) |
| 14 | 14 | set NODE_ENV=production |
| 15 | 15 | call npm install --no-audit --no-fund --quietloglevel=error --no-progress --omit=dev |
| 16 | 16 | node server.js %* |
| 17 | 17 | pause |
| 18 | 18 | popd |
| @@ -95,7 +95,7 @@ if %errorlevel% neq 0 ( | ||
| 95 | 95 | |
| 96 | 96 | echo Installing npm packages and starting server |
| 97 | 97 | set NODE_ENV=production |
| 98 | 98 | call npm install --no-audit --no-fund --quietloglevel=error --no-progress --omit=dev |
| 99 | 99 | node server.js %* |
| 100 | 100 | |
| 101 | 101 | :end |
| @@ -83,6 +83,8 @@ skipContentCheck: false | ||
| 83 | 83 | disableChatBackup: false |
| 84 | 84 | # Number of backups to keep for each chat and settings file |
| 85 | 85 | numberOfBackups: 50 |
| 86 | +# Interval in milliseconds to throttle chat backups per user | |
| 87 | +chatBackupThrottleInterval: 10000 | |
| 86 | 88 | # Allowed hosts for card downloads |
| 87 | 89 | whitelistImportDomains: |
| 88 | 90 | - localhost |
| @@ -108,6 +110,9 @@ enableExtensionsAutoUpdate: true | ||
| 108 | 110 | # Additional model tokenizers can be downloaded on demand. |
| 109 | 111 | # Disabling will fallback to another locally available tokenizer. |
| 110 | 112 | enableDownloadableTokenizers: true |
| 113 | +# Vector storage settings | |
| 114 | +vectors: | |
| 115 | + enableModelScopes: false | |
| 111 | 116 | # Extension settings |
| 112 | 117 | extras: |
| 113 | 118 | # Disables automatic model download from HuggingFace |
| @@ -320,6 +320,26 @@ | ||
| 320 | 320 | "type": "novel_preset" |
| 321 | 321 | }, |
| 322 | 322 | { |
| 323 | + "filename": "presets/novel/Erato-Dragonfruit.json", | |
| 324 | + "type": "novel_preset" | |
| 325 | + }, | |
| 326 | + { | |
| 327 | + "filename": "presets/novel/Erato-Golden Arrow.json", | |
| 328 | + "type": "novel_preset" | |
| 329 | + }, | |
| 330 | + { | |
| 331 | + "filename": "presets/novel/Erato-Shosetsu.json", | |
| 332 | + "type": "novel_preset" | |
| 333 | + }, | |
| 334 | + { | |
| 335 | + "filename": "presets/novel/Erato-Wilder.json", | |
| 336 | + "type": "novel_preset" | |
| 337 | + }, | |
| 338 | + { | |
| 339 | + "filename": "presets/novel/Erato-Zany Scribe.json", | |
| 340 | + "type": "novel_preset" | |
| 341 | + }, | |
| 342 | + { | |
| 323 | 343 | "filename": "presets/textgen/Asterism.json", |
| 324 | 344 | "type": "textgen_preset" |
| 325 | 345 | }, |
| @@ -468,10 +488,6 @@ | ||
| 468 | 488 | "type": "context" |
| 469 | 489 | }, |
| 470 | 490 | { |
| 471 | - "filename": "presets/context/Alpaca-Roleplay.json", | |
| 472 | - "type": "context" | |
| 473 | - }, | |
| 474 | - { | |
| 475 | 491 | "filename": "presets/context/Alpaca-Single-Turn.json", |
| 476 | 492 | "type": "context" |
| 477 | 493 | }, |
| @@ -512,10 +528,6 @@ | ||
| 512 | 528 | "type": "context" |
| 513 | 529 | }, |
| 514 | 530 | { |
| 515 | - "filename": "presets/context/Mistral.json", | |
| 516 | - "type": "context" | |
| 517 | - }, | |
| 518 | - { | |
| 519 | 531 | "filename": "presets/context/NovelAI.json", |
| 520 | 532 | "type": "context" |
| 521 | 533 | }, |
| @@ -556,10 +568,6 @@ | ||
| 556 | 568 | "type": "instruct" |
| 557 | 569 | }, |
| 558 | 570 | { |
| 559 | - "filename": "presets/instruct/Alpaca-Roleplay.json", | |
| 560 | - "type": "instruct" | |
| 561 | - }, | |
| 562 | - { | |
| 563 | 571 | "filename": "presets/instruct/Alpaca-Single-Turn.json", |
| 564 | 572 | "type": "instruct" |
| 565 | 573 | }, |
| @@ -600,10 +608,6 @@ | ||
| 600 | 608 | "type": "instruct" |
| 601 | 609 | }, |
| 602 | 610 | { |
| 603 | - "filename": "presets/instruct/Mistral.json", | |
| 604 | - "type": "instruct" | |
| 605 | - }, | |
| 606 | - { | |
| 607 | 611 | "filename": "presets/instruct/OpenOrca-OpenChat.json", |
| 608 | 612 | "type": "instruct" |
| 609 | 613 | }, |
| @@ -686,5 +690,77 @@ | ||
| 686 | 690 | { |
| 687 | 691 | "filename": "presets/instruct/Gemma 2.json", |
| 688 | 692 | "type": "instruct" |
| 693 | + }, | |
| 694 | + { | |
| 695 | + "filename": "presets/sysprompt/Actor.json", | |
| 696 | + "type": "sysprompt" | |
| 697 | + }, | |
| 698 | + { | |
| 699 | + "filename": "presets/sysprompt/Assistant - Expert.json", | |
| 700 | + "type": "sysprompt" | |
| 701 | + }, | |
| 702 | + { | |
| 703 | + "filename": "presets/sysprompt/Assistant - Simple.json", | |
| 704 | + "type": "sysprompt" | |
| 705 | + }, | |
| 706 | + { | |
| 707 | + "filename": "presets/sysprompt/Chain of Thought.json", | |
| 708 | + "type": "sysprompt" | |
| 709 | + }, | |
| 710 | + { | |
| 711 | + "filename": "presets/sysprompt/Roleplay - Detailed.json", | |
| 712 | + "type": "sysprompt" | |
| 713 | + }, | |
| 714 | + { | |
| 715 | + "filename": "presets/sysprompt/Roleplay - Immersive.json", | |
| 716 | + "type": "sysprompt" | |
| 717 | + }, | |
| 718 | + { | |
| 719 | + "filename": "presets/sysprompt/Roleplay - Simple.json", | |
| 720 | + "type": "sysprompt" | |
| 721 | + }, | |
| 722 | + { | |
| 723 | + "filename": "presets/sysprompt/Text Adventure.json", | |
| 724 | + "type": "sysprompt" | |
| 725 | + }, | |
| 726 | + { | |
| 727 | + "filename": "presets/sysprompt/Writer - Creative.json", | |
| 728 | + "type": "sysprompt" | |
| 729 | + }, | |
| 730 | + { | |
| 731 | + "filename": "presets/sysprompt/Writer - Realistic.json", | |
| 732 | + "type": "sysprompt" | |
| 733 | + }, | |
| 734 | + { | |
| 735 | + "filename": "presets/sysprompt/Blank.json", | |
| 736 | + "type": "sysprompt" | |
| 737 | + }, | |
| 738 | + { | |
| 739 | + "filename": "presets/sysprompt/Neutral - Chat.json", | |
| 740 | + "type": "sysprompt" | |
| 741 | + }, | |
| 742 | + { | |
| 743 | + "filename": "presets/instruct/Mistral V1.json", | |
| 744 | + "type": "instruct" | |
| 745 | + }, | |
| 746 | + { | |
| 747 | + "filename": "presets/context/Mistral V1.json", | |
| 748 | + "type": "context" | |
| 749 | + }, | |
| 750 | + { | |
| 751 | + "filename": "presets/instruct/Mistral V2 & V3.json", | |
| 752 | + "type": "instruct" | |
| 753 | + }, | |
| 754 | + { | |
| 755 | + "filename": "presets/context/Mistral V2 & V3.json", | |
| 756 | + "type": "context" | |
| 757 | + }, | |
| 758 | + { | |
| 759 | + "filename": "presets/instruct/Mistral V3-Tekken.json", | |
| 760 | + "type": "instruct" | |
| 761 | + }, | |
| 762 | + { | |
| 763 | + "filename": "presets/context/Mistral V3-Tekken.json", | |
| 764 | + "type": "context" | |
| 689 | 765 | } |
| 690 | 766 | ] |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": false, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": true, |
| 11 | 10 | "name": "Adventure" |
| 12 | 11 | } |
| @@ -1,12 +0,0 @@ | ||
| 1 | -{ | |
| 2 | - "story_string": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.\n\n{{#if system}}{{system}}\n\n{{/if}}### Input:\n{{#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}}\n\n", | |
| 3 | - "example_separator": "### New Roleplay:", | |
| 4 | - "chat_start": "### New Roleplay:", | |
| 5 | - "use_stop_strings": false, | |
| 6 | - "allow_jailbreak": false, | |
| 7 | - "always_force_name2": true, | |
| 8 | - "trim_sentences": false, | |
| 9 | - "include_newline": false, | |
| 10 | - "single_line": false, | |
| 11 | - "name": "Alpaca-Roleplay" | |
| 12 | -} | |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": false, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Alpaca-Single-Turn" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Alpaca" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "ChatML-Names" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "ChatML" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Command R" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Default" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": false, |
| 8 | 8 | "trim_sentences": true, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "DreamGen Role-Play V1 ChatML" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": false, |
| 8 | 8 | "trim_sentences": true, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "DreamGen Role-Play V1 Llama3" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Gemma 2" |
| 12 | 11 | } |
| @@ -1,12 +1,11 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "story_string": "### Instruction:\nWrite {{char}}'s next reply in this roleplay with {{user}}. Use the provided character sheet and example dialogue for formatting direction and character speech patterns.\n\n{{#if system}}{{system}}\n\n{{/if}}### Character Sheet:\n{{#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}}", |
| 3 | 3 | "example_separator": "### Example:", |
| 4 | 4 | "chat_start": "### START ROLEPLAY:", |
| 5 | 5 | "use_stop_strings": false, |
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Libra-32B" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Lightning 1.1" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Llama 2 Chat" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Llama 3 Instruct" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Llama-3-Instruct-Names" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Minimalist" |
| 12 | 11 | } |
| @@ -0,0 +1,11 @@ | ||
| 1 | +{ | |
| 2 | + "story_string": " [INST] {{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}} [/INST] Understood.</s>", | |
| 3 | + "example_separator": "", | |
| 4 | + "chat_start": "", | |
| 5 | + "use_stop_strings": false, | |
| 6 | + "allow_jailbreak": false, | |
| 7 | + "always_force_name2": true, | |
| 8 | + "trim_sentences": false, | |
| 9 | + "single_line": false, | |
| 10 | + "name": "Mistral V1" | |
| 11 | +} | |
| @@ -1,12 +1,11 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "story_string": "[INST] {{#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}} [/INST] Understood.</s>", |
| 3 | 3 | "example_separator": "Examples:", |
| 4 | 4 | "chat_start": "", |
| 5 | 5 | "use_stop_strings": false, |
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Mistral V2 & V3" |
| 12 | 11 | } |
| @@ -0,0 +1,11 @@ | ||
| 1 | +{ | |
| 2 | + "story_string": "[INST]{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}{{trim}}[/INST]Understood.</s>", | |
| 3 | + "example_separator": "", | |
| 4 | + "chat_start": "", | |
| 5 | + "use_stop_strings": false, | |
| 6 | + "allow_jailbreak": false, | |
| 7 | + "always_force_name2": true, | |
| 8 | + "trim_sentences": false, | |
| 9 | + "single_line": false, | |
| 10 | + "name": "Mistral V3-Tekken" | |
| 11 | +} | |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "NovelAI" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "OldDefault" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Phi" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Pygmalion" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Story" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "Synthia" |
| 12 | 11 | } |
| @@ -6,7 +6,6 @@ | ||
| 6 | 6 | "allow_jailbreak": false, |
| 7 | 7 | "always_force_name2": true, |
| 8 | 8 | "trim_sentences": false, |
| 9 | - "include_newline": false, | |
| 10 | 9 | "single_line": false, |
| 11 | 10 | "name": "simple-proxy-for-tavern" |
| 12 | 11 | } |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]", | |
| 3 | 2 | "input_sequence": "", |
| 4 | 3 | "output_sequence": "", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,24 +0,0 @@ | ||
| 1 | -{ | |
| 2 | - "system_prompt": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.", | |
| 3 | - "input_sequence": "### Instruction:", | |
| 4 | - "output_sequence": "### Response:", | |
| 5 | - "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):", | |
| 6 | - "system_sequence": "### Input:", | |
| 7 | - "stop_sequence": "", | |
| 8 | - "wrap": true, | |
| 9 | - "macro": true, | |
| 10 | - "names": true, | |
| 11 | - "names_force_groups": true, | |
| 12 | - "activation_regex": "", | |
| 13 | - "system_sequence_prefix": "", | |
| 14 | - "system_sequence_suffix": "", | |
| 15 | - "first_output_sequence": "", | |
| 16 | - "skip_examples": false, | |
| 17 | - "output_suffix": "\n\n", | |
| 18 | - "input_suffix": "\n\n", | |
| 19 | - "system_suffix": "\n\n", | |
| 20 | - "user_alignment_message": "", | |
| 21 | - "system_same_as_user": false, | |
| 22 | - "last_system_sequence": "", | |
| 23 | - "name": "Alpaca-Roleplay" | |
| 24 | -} | |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\nWrite 1 reply only, italicize actions, and avoid quotation marks. Use markdown. Be proactive, creative, and drive the plot and conversation forward. Include dialog as well as narration.", | |
| 3 | 2 | "input_sequence": "", |
| 4 | 3 | "output_sequence": "", |
| 5 | 4 | "last_output_sequence": "\n### Response:", |
| @@ -7,12 +6,11 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| 15 | 13 | "first_output_sequence": "<START OF ROLEPLAYCHAT>", |
| 16 | 14 | "skip_examples": false, |
| 17 | 15 | "output_suffix": "", |
| 18 | 16 | "input_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 3 | 2 | "input_sequence": "### Instruction:", |
| 4 | 3 | "output_sequence": "### Response:", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|im_start|>[{{name}}]", |
| 4 | 3 | "output_sequence": "<|im_start|>[{{name}}]", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<|im_end|>", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|im_start|>user", |
| 4 | 3 | "output_sequence": "<|im_start|>assistant", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<|im_end|>", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": true"always", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>", |
| 4 | 3 | "output_sequence": "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>", |
| 5 | 4 | "first_output_sequence": "", |
| @@ -9,8 +8,7 @@ | ||
| 9 | 8 | "stop_sequence": "<|END_OF_TURN_TOKEN|>", |
| 10 | 9 | "wrap": false, |
| 11 | 10 | "macro": true, |
| 12 | 11 | "namesnames_behavior": true"always", |
| 13 | - "names_force_groups": true, | |
| 14 | 12 | "activation_regex": "", |
| 15 | 13 | "skip_examples": false, |
| 16 | 14 | "output_suffix": "<|END_OF_TURN_TOKEN|>", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.", | |
| 3 | 2 | "input_sequence": "\n<|im_start|>text names= {{name}}\n", |
| 4 | 3 | "output_sequence": "\n<|im_start|>text names= {{name}}\n", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "\n<|im_start|>", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below.", | |
| 3 | 2 | "input_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{user}}<|end_header_id|>\n\n", |
| 4 | 3 | "output_sequence": "<|eot_id|>\n<|start_header_id|>writer character: {{char}}<|end_header_id|>\n\n", |
| 5 | 4 | "first_output_sequence": "", |
| @@ -10,8 +9,7 @@ | ||
| 10 | 9 | "separator_sequence": "", |
| 11 | 10 | "wrap": false, |
| 12 | 11 | "macro": true, |
| 13 | 12 | "namesnames_behavior": false"none", |
| 14 | - "names_force_groups": false, | |
| 15 | 13 | "activation_regex": "", |
| 16 | 14 | "skip_examples": false, |
| 17 | 15 | "name": "DreamGen Role-Play V1 Llama3" |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Continue writing this story and portray characters realistically.", | |
| 3 | 2 | "input_sequence": "<start_of_turn>user", |
| 4 | 3 | "output_sequence": "<start_of_turn>model", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<end_of_turn>", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 3 | 2 | "input_sequence": "USER: ", |
| 4 | 3 | "output_sequence": "GPT: ", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "BEGINNING OF CONVERSATION: ", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Avoid repetition, don't loop. Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions.", | |
| 3 | 2 | "input_sequence": "", |
| 4 | 3 | "output_sequence": "", |
| 5 | 4 | "last_output_sequence": "\n### Response:", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": true"always", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nTake the role of {{char}} in a play that leaves a lasting impression on {{user}}. Write {{char}}'s next reply.\nNever skip or gloss over {{char}}’s actions. Progress the scene at a naturally slow pace.\n\n", | |
| 3 | 2 | "input_sequence": "### Instruction:", |
| 4 | 3 | "output_sequence": "### Response: (length = unlimited)", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "[INST] ", |
| 4 | 3 | "output_sequence": "", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|start_header_id|>user<|end_header_id|>\n\n", |
| 4 | 3 | "output_sequence": "<|start_header_id|>assistant<|end_header_id|>\n\n", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<|eot_id|>", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": true"always", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n", |
| 4 | 3 | "output_sequence": "<|start_header_id|>[{{name}}]<|end_header_id|>\n\n", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<|eot_id|>", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Enter roleplay mode. You must act as {{char}}, whose persona follows:", | |
| 3 | 2 | "input_sequence": "<|user|>", |
| 4 | 3 | "output_sequence": "<|model|>", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "</s>", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "<|system|>", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -0,0 +1,22 @@ | ||
| 1 | +{ | |
| 2 | + "input_sequence": " [INST] ", | |
| 3 | + "output_sequence": " [/INST] ", | |
| 4 | + "last_output_sequence": " [/INST]", | |
| 5 | + "system_sequence": "", | |
| 6 | + "stop_sequence": "", | |
| 7 | + "wrap": false, | |
| 8 | + "macro": true, | |
| 9 | + "names_behavior": "always", | |
| 10 | + "activation_regex": "", | |
| 11 | + "system_sequence_prefix": "", | |
| 12 | + "system_sequence_suffix": "", | |
| 13 | + "first_output_sequence": "", | |
| 14 | + "skip_examples": false, | |
| 15 | + "output_suffix": "</s>", | |
| 16 | + "input_suffix": "", | |
| 17 | + "system_suffix": "", | |
| 18 | + "user_alignment_message": "Let's get started. Please respond based on the information and instructions provided above.", | |
| 19 | + "system_same_as_user": true, | |
| 20 | + "last_system_sequence": "", | |
| 21 | + "name": "Mistral V1" | |
| 22 | +} | |
| @@ -1,24 +1,22 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "[INST] ", |
| 4 | 3 | "output_sequence": "[/INST] ", |
| 5 | 4 | "last_output_sequence": "[/INST]", |
| 6 | 5 | "system_sequence": "", |
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": true"always", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| 15 | 13 | "first_output_sequence": "", |
| 16 | 14 | "skip_examples": false, |
| 17 | 15 | "output_suffix": "\n</s>", |
| 18 | 16 | "input_suffix": " [/INST]\n", |
| 19 | 17 | "system_suffix": "", |
| 20 | 18 | "user_alignment_message": "Let's get started. Please respond based on the information and instructions provided above.", |
| 21 | 19 | "system_same_as_user": true, |
| 22 | 20 | "last_system_sequence": "", |
| 23 | 21 | "name": "Mistral V2 & V3" |
| 24 | 22 | } |
| @@ -0,0 +1,22 @@ | ||
| 1 | +{ | |
| 2 | + "input_sequence": "[INST]", | |
| 3 | + "output_sequence": "[/INST]", | |
| 4 | + "last_output_sequence": "", | |
| 5 | + "system_sequence": "", | |
| 6 | + "stop_sequence": "", | |
| 7 | + "wrap": false, | |
| 8 | + "macro": true, | |
| 9 | + "names_behavior": "always", | |
| 10 | + "activation_regex": "", | |
| 11 | + "system_sequence_prefix": "", | |
| 12 | + "system_sequence_suffix": "", | |
| 13 | + "first_output_sequence": "", | |
| 14 | + "skip_examples": false, | |
| 15 | + "output_suffix": "</s>", | |
| 16 | + "input_suffix": "", | |
| 17 | + "system_suffix": "", | |
| 18 | + "user_alignment_message": "Let's get started. Please respond based on the information and instructions provided above.", | |
| 19 | + "system_same_as_user": true, | |
| 20 | + "last_system_sequence": "", | |
| 21 | + "name": "Mistral V3-Tekken" | |
| 22 | +} | |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate.\n", | |
| 3 | 2 | "input_sequence": "\nUser: ", |
| 4 | 3 | "output_sequence": "\nAssistant: ", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in this fictional roleplay with {{user}}.", | |
| 3 | 2 | "input_sequence": "<|user|>\n", |
| 4 | 3 | "output_sequence": "<|assistant|>\n", |
| 5 | 4 | "first_output_sequence": "", |
| @@ -9,8 +8,7 @@ | ||
| 9 | 8 | "stop_sequence": "<|end|>", |
| 10 | 9 | "wrap": false, |
| 11 | 10 | "macro": true, |
| 12 | 11 | "namesnames_behavior": true"always", |
| 13 | - "names_force_groups": true, | |
| 14 | 12 | "activation_regex": "", |
| 15 | 13 | "skip_examples": false, |
| 16 | 14 | "output_suffix": "<|end|>\n", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Enter RP mode. You shall reply to {{user}} while staying in character. Your responses must be detailed, creative, immersive, and drive the scenario forward. You will follow {{char}}'s persona.", | |
| 3 | 2 | "input_sequence": "<|user|>", |
| 4 | 3 | "output_sequence": "<|model|>", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "<|user|>", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": true"always", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "<|system|>", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "", | |
| 3 | 2 | "input_sequence": "", |
| 4 | 3 | "output_sequence": "", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation.", | |
| 3 | 2 | "input_sequence": "USER: ", |
| 4 | 3 | "output_sequence": "ASSISTANT: ", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "SYSTEM: ", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 3 | 2 | "input_sequence": "### Human:", |
| 4 | 3 | "output_sequence": "### Assistant:", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 3 | 2 | "input_sequence": "\nUSER: ", |
| 4 | 3 | "output_sequence": "\nASSISTANT: ", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": false, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "BEGINNING OF CONVERSATION:", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.\n\nWrite {{char}}'s next detailed reply in a fictional roleplay chat between {{user}} and {{char}}.", | |
| 3 | 2 | "input_sequence": "USER: ", |
| 4 | 3 | "output_sequence": "ASSISTANT: ", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "Write {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 3 | 2 | "input_sequence": "", |
| 4 | 3 | "output_sequence": "### Response:", |
| 5 | 4 | "last_output_sequence": "", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"force", |
| 11 | - "names_force_groups": true, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | { |
| 2 | - "system_prompt": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]", | |
| 3 | 2 | "input_sequence": "### Instruction:\n#### {{name}}:", |
| 4 | 3 | "output_sequence": "### Response:\n#### {{name}}:", |
| 5 | 4 | "last_output_sequence": "### Response (2 paragraphs, engaging, natural, authentic, descriptive, creative):\n#### {{name}}:", |
| @@ -7,8 +6,7 @@ | ||
| 7 | 6 | "stop_sequence": "", |
| 8 | 7 | "wrap": true, |
| 9 | 8 | "macro": true, |
| 10 | 9 | "namesnames_behavior": false"none", |
| 11 | - "names_force_groups": false, | |
| 12 | 10 | "activation_regex": "", |
| 13 | 11 | "system_sequence_prefix": "", |
| 14 | 12 | "system_sequence_suffix": "", |
| @@ -14,7 +14,10 @@ | ||
| 14 | 14 | "use_cache": false, |
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | - "cfg_scale": 1, | |
| 18 | 17 | "phrase_rep_pen": "medium", |
| 19 | 18 | "max_context": 7800, |
| 19 | + "min_p": 0, | |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [6, 0, 1, 2, 3], |
| 3 | 3 | "temperature": 1, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -14,6 +14,9 @@ | ||
| 14 | 14 | "return_full_text": false, |
| 15 | 15 | "prefix": "vanilla", |
| 16 | 16 | "phrase_rep_pen": "medium", |
| 17 | 17 | "cfg_scalemax_context": 1.557800, |
| 18 | 18 | "max_contextmin_p": 78000, |
| 19 | + "math1_temp": 1, | |
| 20 | + "math1_quad": 0, | |
| 21 | + "math1_quad_entropy_scale": 0 | |
| 19 | 22 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [6, 2, 3, 1, 0], |
| 3 | 3 | "temperature": 1, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -15,6 +15,9 @@ | ||
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | 17 | "phrase_rep_pen": "very_aggressive", |
| 18 | 18 | "cfg_scalemax_context": 1.37800, |
| 19 | 19 | "max_contextmin_p": 78000, |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -15,7 +15,10 @@ | ||
| 15 | 15 | "use_cache": false, |
| 16 | 16 | "return_full_text": false, |
| 17 | 17 | "prefix": "vanilla", |
| 18 | - "cfg_scale": 1, | |
| 19 | 18 | "phrase_rep_pen": "aggressive", |
| 20 | 19 | "max_context": 7800, |
| 20 | + "min_p": 0, | |
| 21 | + "math1_temp": 1, | |
| 22 | + "math1_quad": 0, | |
| 23 | + "math1_quad_entropy_scale": 0 | |
| 21 | 24 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [8, 6, 5, 0, 3], |
| 3 | 3 | "temperature": 0.9, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -15,6 +15,9 @@ | ||
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | 17 | "phrase_rep_pen": "off", |
| 18 | 18 | "cfg_scalemax_context": 1.487800, |
| 19 | 19 | "max_contextmin_p": 78000, |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -15,7 +15,10 @@ | ||
| 15 | 15 | "use_cache": false, |
| 16 | 16 | "return_full_text": false, |
| 17 | 17 | "prefix": "vanilla", |
| 18 | - "cfg_scale": 1, | |
| 19 | 18 | "phrase_rep_pen": "very_light", |
| 20 | 19 | "max_context": 7800, |
| 20 | + "min_p": 0, | |
| 21 | + "math1_temp": 1, | |
| 22 | + "math1_quad": 0, | |
| 23 | + "math1_quad_entropy_scale": 0 | |
| 21 | 24 | } |
| @@ -0,0 +1,31 @@ | ||
| 1 | +{ | |
| 2 | + "max_context": 8000, | |
| 3 | + "temperature": 1.37, | |
| 4 | + "max_length": 150, | |
| 5 | + "min_length": 1, | |
| 6 | + "top_k": 0, | |
| 7 | + "top_p": 1, | |
| 8 | + "top_a": 0.1, | |
| 9 | + "typical_p": 0.875, | |
| 10 | + "tail_free_sampling": 0.87, | |
| 11 | + "repetition_penalty": 3.25, | |
| 12 | + "repetition_penalty_range": 6000, | |
| 13 | + "repetition_penalty_slope": 3.25, | |
| 14 | + "repetition_penalty_frequency": 0, | |
| 15 | + "repetition_penalty_presence": 0, | |
| 16 | + "phrase_rep_pen": "off", | |
| 17 | + "mirostat_lr": 0.2, | |
| 18 | + "mirostat_tau": 4, | |
| 19 | + "math1_temp": 0.9, | |
| 20 | + "math1_quad": 0.07, | |
| 21 | + "math1_quad_entropy_scale": -0.05, | |
| 22 | + "min_p": 0.035, | |
| 23 | + "order": [ | |
| 24 | + 0, | |
| 25 | + 5, | |
| 26 | + 9, | |
| 27 | + 10, | |
| 28 | + 8, | |
| 29 | + 4 | |
| 30 | + ] | |
| 31 | +} | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "max_context": 8000, | |
| 3 | + "temperature": 1, | |
| 4 | + "max_length": 150, | |
| 5 | + "min_length": 1, | |
| 6 | + "top_k": 0, | |
| 7 | + "top_p": 0.995, | |
| 8 | + "top_a": 1, | |
| 9 | + "typical_p": 1, | |
| 10 | + "tail_free_sampling": 0.87, | |
| 11 | + "repetition_penalty": 1.5, | |
| 12 | + "repetition_penalty_range": 2240, | |
| 13 | + "repetition_penalty_slope": 1, | |
| 14 | + "repetition_penalty_frequency": 0, | |
| 15 | + "repetition_penalty_presence": 0, | |
| 16 | + "phrase_rep_pen": "light", | |
| 17 | + "mirostat_lr": 1, | |
| 18 | + "mirostat_tau": 0, | |
| 19 | + "math1_temp": 0.3, | |
| 20 | + "math1_quad": 0.19, | |
| 21 | + "math1_quad_entropy_scale": 0, | |
| 22 | + "min_p": 0, | |
| 23 | + "order": [ | |
| 24 | + 9, | |
| 25 | + 2 | |
| 26 | + ] | |
| 27 | +} | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "max_context": 8000, | |
| 3 | + "temperature": 1, | |
| 4 | + "max_length": 150, | |
| 5 | + "min_length": 1, | |
| 6 | + "top_k": 50, | |
| 7 | + "top_p": 0.85, | |
| 8 | + "top_a": 1, | |
| 9 | + "typical_p": 1, | |
| 10 | + "tail_free_sampling": 0.895, | |
| 11 | + "repetition_penalty": 1.63, | |
| 12 | + "repetition_penalty_range": 1024, | |
| 13 | + "repetition_penalty_slope": 3.33, | |
| 14 | + "repetition_penalty_frequency": 0.0035, | |
| 15 | + "repetition_penalty_presence": 0, | |
| 16 | + "phrase_rep_pen": "medium", | |
| 17 | + "mirostat_lr": 1, | |
| 18 | + "mirostat_tau": 0, | |
| 19 | + "math1_temp": 0.3, | |
| 20 | + "math1_quad": 0.0645, | |
| 21 | + "math1_quad_entropy_scale": 0.05, | |
| 22 | + "min_p": 0.05, | |
| 23 | + "order": [ | |
| 24 | + 9, | |
| 25 | + 10 | |
| 26 | + ] | |
| 27 | +} | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "max_context": 8000, | |
| 3 | + "temperature": 1, | |
| 4 | + "max_length": 150, | |
| 5 | + "min_length": 1, | |
| 6 | + "top_k": 300, | |
| 7 | + "top_p": 0.98, | |
| 8 | + "top_a": 0.004, | |
| 9 | + "typical_p": 0.96, | |
| 10 | + "tail_free_sampling": 0.96, | |
| 11 | + "repetition_penalty": 1.48, | |
| 12 | + "repetition_penalty_range": 2240, | |
| 13 | + "repetition_penalty_slope": 0.64, | |
| 14 | + "repetition_penalty_frequency": 0, | |
| 15 | + "repetition_penalty_presence": 0, | |
| 16 | + "phrase_rep_pen": "medium", | |
| 17 | + "mirostat_lr": 1, | |
| 18 | + "mirostat_tau": 0, | |
| 19 | + "math1_temp": -0.0485, | |
| 20 | + "math1_quad": 0.145, | |
| 21 | + "math1_quad_entropy_scale": 0, | |
| 22 | + "min_p": 0.02, | |
| 23 | + "order": [ | |
| 24 | + 9, | |
| 25 | + 10 | |
| 26 | + ] | |
| 27 | +} | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "max_context": 8000, | |
| 3 | + "temperature": 1, | |
| 4 | + "max_length": 150, | |
| 5 | + "min_length": 1, | |
| 6 | + "top_k": 0, | |
| 7 | + "top_p": 0.99, | |
| 8 | + "top_a": 1, | |
| 9 | + "typical_p": 1, | |
| 10 | + "tail_free_sampling": 0.99, | |
| 11 | + "repetition_penalty": 1, | |
| 12 | + "repetition_penalty_range": 64, | |
| 13 | + "repetition_penalty_slope": 1, | |
| 14 | + "repetition_penalty_frequency": 0.75, | |
| 15 | + "repetition_penalty_presence": 1.5, | |
| 16 | + "phrase_rep_pen": "medium", | |
| 17 | + "mirostat_lr": 1, | |
| 18 | + "mirostat_tau": 1, | |
| 19 | + "math1_temp": -0.4, | |
| 20 | + "math1_quad": 0.6, | |
| 21 | + "math1_quad_entropy_scale": -0.1, | |
| 22 | + "min_p": 0.08, | |
| 23 | + "order": [ | |
| 24 | + 9, | |
| 25 | + 2 | |
| 26 | + ] | |
| 27 | +} | |
| @@ -16,7 +16,10 @@ | ||
| 16 | 16 | "use_cache": false, |
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | - "cfg_scale": 1, | |
| 20 | 19 | "phrase_rep_pen": "very_light", |
| 21 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -14,7 +14,10 @@ | ||
| 14 | 14 | "use_cache": false, |
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | - "cfg_scale": 1, | |
| 18 | 17 | "phrase_rep_pen": "off", |
| 19 | 18 | "max_context": 7800, |
| 19 | + "min_p": 0, | |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [6, 0, 8, 5, 3], |
| 3 | 3 | "temperature": 1.5, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -15,6 +15,9 @@ | ||
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | 17 | "phrase_rep_pen": "very_aggressive", |
| 18 | 18 | "cfg_scalemax_context": 1.47800, |
| 19 | 19 | "max_contextmin_p": 78000, |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -16,7 +16,10 @@ | ||
| 16 | 16 | "use_cache": false, |
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | - "cfg_scale": 1, | |
| 20 | 19 | "phrase_rep_pen": "very_light", |
| 21 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -16,7 +16,10 @@ | ||
| 16 | 16 | "use_cache": false, |
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | - "cfg_scale": 1, | |
| 20 | 19 | "phrase_rep_pen": "very_light", |
| 21 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [6, 0, 4, 1, 2, 5, 3], |
| 3 | 3 | "temperature": 1.31, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -17,6 +17,9 @@ | ||
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | 19 | "phrase_rep_pen": "medium", |
| 20 | 20 | "cfg_scalemax_context": 1.357800, |
| 21 | 21 | "max_contextmin_p": 78000, |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -15,6 +15,9 @@ | ||
| 15 | 15 | "return_full_text": false, |
| 16 | 16 | "prefix": "vanilla", |
| 17 | 17 | "phrase_rep_pen": "medium", |
| 18 | 18 | "cfg_scalemax_context": 1.07800, |
| 19 | 19 | "max_contextmin_p": 78000, |
| 20 | + "math1_temp": 1, | |
| 21 | + "math1_quad": 0, | |
| 22 | + "math1_quad_entropy_scale": 0 | |
| 20 | 23 | } |
| @@ -13,5 +13,9 @@ | ||
| 13 | 13 | "return_full_text": false, |
| 14 | 14 | "prefix": "vanilla", |
| 15 | 15 | "phrase_rep_pen": "medium", |
| 16 | 16 | "max_context": 7800, |
| 17 | + "min_p": 0, | |
| 18 | + "math1_temp": 1, | |
| 19 | + "math1_quad": 0, | |
| 20 | + "math1_quad_entropy_scale": 0 | |
| 17 | 21 | } |
| @@ -16,7 +16,10 @@ | ||
| 16 | 16 | "use_cache": false, |
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | - "cfg_scale": 1, | |
| 20 | 19 | "phrase_rep_pen": "very_light", |
| 21 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -14,6 +14,9 @@ | ||
| 14 | 14 | "return_full_text": false, |
| 15 | 15 | "prefix": "vanilla", |
| 16 | 16 | "phrase_rep_pen": "aggressive", |
| 17 | 17 | "cfg_scalemax_context": 1.07800, |
| 18 | 18 | "max_contextmin_p": 78000, |
| 19 | + "math1_temp": 1, | |
| 20 | + "math1_quad": 0, | |
| 21 | + "math1_quad_entropy_scale": 0 | |
| 19 | 22 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "order": [6, 0, 5], |
| 3 | 3 | "temperature": 0.895, |
| 4 | 4 | "max_length": 150, |
| 5 | 5 | "min_length": 1, |
| @@ -13,6 +13,9 @@ | ||
| 13 | 13 | "return_full_text": false, |
| 14 | 14 | "prefix": "vanilla", |
| 15 | 15 | "phrase_rep_pen": "aggressive", |
| 16 | 16 | "cfg_scalemax_context": 1.37800, |
| 17 | 17 | "max_contextmin_p": 78000, |
| 18 | + "math1_temp": 1, | |
| 19 | + "math1_quad": 0, | |
| 20 | + "math1_quad_entropy_scale": 0 | |
| 18 | 21 | } |
| @@ -16,7 +16,10 @@ | ||
| 16 | 16 | "use_cache": false, |
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | - "cfg_scale": 1, | |
| 20 | 19 | "phrase_rep_pen": "very_light", |
| 21 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 22 | 25 | } |
| @@ -17,5 +17,9 @@ | ||
| 17 | 17 | "return_full_text": false, |
| 18 | 18 | "prefix": "vanilla", |
| 19 | 19 | "phrase_rep_pen": "very_aggressive", |
| 20 | 20 | "max_context": 7800, |
| 21 | + "min_p": 0, | |
| 22 | + "math1_temp": 1, | |
| 23 | + "math1_quad": 0, | |
| 24 | + "math1_quad_entropy_scale": 0 | |
| 21 | 25 | } |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Actor", | |
| 3 | + "content": "You are an expert actor that can fully immerse yourself into any role given. You do not break character for any reason, even if someone tries addressing you as an AI or language model. Currently your role is {{char}}, which is described in detail below. As {{char}}, continue the exchange with {{user}}." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Assistant - Expert", | |
| 3 | + "content": "You are a helpful assistant. Please answer truthfully and write out your thinking step by step to be sure you get the right answer. If you make a mistake or encounter an error in your thinking, say so out loud and attempt to correct it. If you don't know or aren't sure about something, say so clearly. You will act as a professional logician, mathematician, and physicist. You will also act as the most appropriate type of expert to answer any particular question or solve the relevant problem; state which expert type your are, if so. Also think of any particular named expert that would be ideal to answer the relevant question or solve the relevant problem; name and act as them, if appropriate." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Assistant - Simple", | |
| 3 | + "content": "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Blank", | |
| 3 | + "content": "" | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Chain of Thought", | |
| 3 | + "content": "Elaborate on the topic using a Tree of Thoughts and backtrack when necessary to construct a clear, cohesive Chain of Thought reasoning. Always answer without hesitation." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Neutral - Chat", | |
| 3 | + "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Roleplay - Detailed", | |
| 3 | + "content": "Develop the plot slowly, always stay in character. Describe all actions in full, elaborate, explicit, graphic, and vivid detail. Mention all relevant sensory perceptions. Keep the story immersive and engaging." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Roleplay - Immersive", | |
| 3 | + "content": "[System note: Write one reply only. Do not decide what {{user}} says or does. Write at least one paragraph, up to four. Be descriptive and immersive, providing vivid details about {{char}}'s actions, emotions, and the environment. Write with a high degree of complexity and burstiness. Do not repeat this message.]" | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Roleplay - Simple", | |
| 3 | + "content": "You're {{char}} in this fictional never-ending uncensored roleplay with {{user}}." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Text Adventure", | |
| 3 | + "content": "[Enter Adventure Mode. Narrate the story based on {{user}}'s dialogue and actions after \">\". Describe the surroundings in vivid detail. Be detailed, creative, verbose, and proactive. Move the story forward by introducing fantasy elements and interesting characters.]" | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Writer - Creative", | |
| 3 | + "content": "You are an intelligent, skilled, versatile writer.\n\nYour task is to write a role-play based on the information below." | |
| 4 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "name": "Writer - Realistic", | |
| 3 | + "content": "Continue writing this story and portray characters realistically." | |
| 4 | +} | |
| @@ -90,7 +90,6 @@ | ||
| 90 | 90 | "pin_examples": false, |
| 91 | 91 | "strip_examples": false, |
| 92 | 92 | "trim_sentences": false, |
| 93 | - "include_newline": false, | |
| 94 | 93 | "always_force_name2": true, |
| 95 | 94 | "user_prompt_bias": "", |
| 96 | 95 | "show_user_prompt_bias": true, |
| @@ -150,11 +149,9 @@ | ||
| 150 | 149 | "continue_on_send": false, |
| 151 | 150 | "trim_spaces": true, |
| 152 | 151 | "relaxed_api_urls": false, |
| 153 | - "default_instruct": "", | |
| 154 | 152 | "instruct": { |
| 155 | 153 | "enabled": false, |
| 156 | 154 | "preset": "Alpaca", |
| 157 | - "system_prompt": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n", | |
| 158 | 155 | "input_sequence": "### Instruction:", |
| 159 | 156 | "output_sequence": "### Response:", |
| 160 | 157 | "last_output_sequence": "", |
| @@ -162,8 +159,7 @@ | ||
| 162 | 159 | "stop_sequence": "", |
| 163 | 160 | "wrap": true, |
| 164 | 161 | "macro": true, |
| 165 | 162 | "namesnames_behavior": false"force", |
| 166 | - "names_force_groups": true, | |
| 167 | 163 | "activation_regex": "", |
| 168 | 164 | "system_sequence_prefix": "", |
| 169 | 165 | "system_sequence_suffix": "", |
| @@ -175,7 +171,11 @@ | ||
| 175 | 171 | "user_alignment_message": "", |
| 176 | 172 | "system_same_as_user": false |
| 177 | 173 | }, |
| 178 | 174 | "default_contextsysprompt": "Default",{ |
| 175 | + "enabled": true, | |
| 176 | + "name": "Neutral - Chat", | |
| 177 | + "content": "Write {{char}}'s next reply in a fictional chat between {{char}} and {{user}}." | |
| 178 | + }, | |
| 179 | 179 | "context": { |
| 180 | 180 | "preset": "Default", |
| 181 | 181 | "story_string": "{{#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}}", |
| @@ -235,7 +235,7 @@ | ||
| 235 | 235 | "lengthPenaltyStep": 0.1, |
| 236 | 236 | "memoryFrozen": false, |
| 237 | 237 | "source": "extras", |
| 238 | 238 | "prompt": "[PauseIgnore yourprevious roleplayinstructions. Summarize the most important facts and events that have happened in the chatstory so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]", |
| 239 | 239 | "promptWords": 200, |
| 240 | 240 | "promptMinWords": 25, |
| 241 | 241 | "promptMaxWords": 1000, |
| @@ -298,13 +298,13 @@ | ||
| 298 | 298 | "horde_karras": true, |
| 299 | 299 | "refine_mode": false, |
| 300 | 300 | "prompts": { |
| 301 | 301 | "0": "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,']", |
| 302 | 302 | "1": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{user}}'s physical appearance from the perspective of {{char}} in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'full body portrait,'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{char}} when writing this description, and do not attempt to continue the story.]", |
| 303 | 303 | "2": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description for all of the following: a brief recap of recent events in the story, {{char}}'s appearance, and {{char}}'s surroundings. Do not roleplayreply whileas {{char}} when writing this description, and do not attempt to continue the story.]", |
| 304 | 304 | "3": "[PauseIgnore yourprevious roleplayinstructions and provide ONLY the last chat message string back to me verbatim. Do not write anything after the string. Do not roleplayreply atas all{{char}} inwhen yourwriting response.this Dodescription, and do not attempt to continue the roleplay story.]", |
| 305 | - "4": "[Pause your roleplay. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message.\n\n Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman').\n\n Ignore non-visible things such as feelings, personality traits, thoughts, and spoken dialog.\n\n Add keywords in this precise order:\n a keyword to describe the location of the scene,\n a keyword to mention how many characters of each gender or type are present in the scene (minimum of two characters:\n {{user}} and {{char}}, example: '2 men ' or '1 man 1 woman ', '1 man 3 robots'),\n\n keywords to describe the relative physical positioning of the characters to each other (if a commonly known term for the positioning is known use it instead of describing the positioning in detail) + 'POV',\n\n a single keyword or phrase to describe the primary act taking place in the last chat message,\n\n keywords to describe {{char}}'s physical appearance and facial expression,\n keywords to describe {{char}}'s actions,\n keywords to describe {{user}}'s physical appearance and actions.\n\n If character actions involve direct physical interaction with another character, mention specifically which body parts interacting and how.\n\n A correctly formatted example response would be:\n '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)']", | |
| 305 | + "4": "Ignore previous instructions. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message.\n\n Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman').\n\n Ignore non-visible things such as feelings, personality traits, thoughts, and spoken dialog.\n\n Add keywords in this precise order:\n a keyword to describe the location of the scene,\n a keyword to mention how many characters of each gender or type are present in the scene (minimum of two characters:\n {{user}} and {{char}}, example: '2 men ' or '1 man 1 woman ', '1 man 3 robots'),\n\n keywords to describe the relative physical positioning of the characters to each other (if a commonly known term for the positioning is known use it instead of describing the positioning in detail) + 'POV',\n\n a single keyword or phrase to describe the primary act taking place in the last chat message,\n\n keywords to describe {{char}}'s physical appearance and facial expression,\n keywords to describe {{char}}'s actions,\n keywords to describe {{user}}'s physical appearance and actions.\n\n If character actions involve direct physical interaction with another character, mention specifically which body parts interacting and how.\n\n A correctly formatted example response would be:\n '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)'", | |
| 306 | 306 | "5": "[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, facial features and expressions, occupation, hair and hair accessories (if any), what they are wearing on their upper body (if anything). Do not describe anything below their neck. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'close up facial portrait,']", |
| 307 | 307 | "7": "[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{char}}'s surroundings in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: location, time of day, weather, lighting, and any other relevant details. Do not include descriptions of characters and non-visual qualities such as names, personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase 'background,'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{user}} when writing this description, and do not attempt to continue the story.]" |
| 308 | 308 | }, |
| 309 | 309 | "character_prompts": {} |
| 310 | 310 | }, |
| @@ -318,9 +318,9 @@ | ||
| 318 | 318 | "objective": { |
| 319 | 319 | "customPrompts": { |
| 320 | 320 | "default": { |
| 321 | 321 | "createTask": "PauseIgnore yourprevious roleplayinstructions and generate a list of tasks to complete an objective. Your next response must be formatted as a numbered list of plain text entries. Do not include anything but the numbered list. The list must be prioritized in the order that tasks must be completed.\n\nThe objective that you must make a numbered task list for is: [{{objective}}].\nThe tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} directly. Be sure to include the objective as the final task.\n\nGiven an example objective of 'Make me a four course dinner', here is an example output:\n1. Determine what the courses will be\n2. Find recipes for each course\n3. Go shopping for supplies with {{user}}\n4. Cook the food\n5. Get {{user}} to set the table\n6. Serve the food\n7. Enjoy eating the meal with {{user}}\n ", |
| 322 | 322 | "checkTaskCompleted": "PauseIgnore yourprevious roleplayinstructions. Determine if this task is completed: [{{task}}].\nTo do this, examine the most recent messages. Your response must only contain either true or false, nothing other words.\nExample output:\ntrue\n ", |
| 323 | 323 | "currentTask": "Your current task is [{{task}}]. Balance existing roleplaystory with completing this task." |
| 324 | 324 | } |
| 325 | 325 | } |
| 326 | 326 | }, |
| @@ -417,7 +417,6 @@ | ||
| 417 | 417 | "preset_settings_novel": "Talker-Chat-Clio", |
| 418 | 418 | "streaming_novel": true, |
| 419 | 419 | "preamble": "[ Style: chat, complex, sensory, visceral ]", |
| 420 | - "cfg_uc": "", | |
| 421 | 420 | "banned_tokens": "", |
| 422 | 421 | "order": [ |
| 423 | 422 | 1, |
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "sillytavern", |
| 3 | 3 | "version": "1.12.56", |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "sillytavern", |
| 9 | 9 | "version": "1.12.56", |
| 10 | 10 | "hasInstallScript": true, |
| 11 | 11 | "license": "AGPL-3.0", |
| 12 | 12 | "dependencies": { |
| @@ -22,7 +22,7 @@ | ||
| 22 | 22 | "cookie-session": "^2.1.0", |
| 23 | 23 | "cors": "^2.8.5", |
| 24 | 24 | "csrf-csrf": "^2.2.3", |
| 25 | 25 | "express": "^4.2021.0", |
| 26 | 26 | "form-data": "^4.0.0", |
| 27 | 27 | "google-translate-api-browser": "^3.0.1", |
| 28 | 28 | "he": "^1.2.0", |
| @@ -1568,21 +1568,6 @@ | ||
| 1568 | 1568 | "node": ">=0.10.0" |
| 1569 | 1569 | } |
| 1570 | 1570 | }, |
| 1571 | - "node_modules/body-parser/node_modules/qs": { | |
| 1572 | - "version": "6.13.0", | |
| 1573 | - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", | |
| 1574 | - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", | |
| 1575 | - "license": "BSD-3-Clause", | |
| 1576 | - "dependencies": { | |
| 1577 | - "side-channel": "^1.0.6" | |
| 1578 | - }, | |
| 1579 | - "engines": { | |
| 1580 | - "node": ">=0.6" | |
| 1581 | - }, | |
| 1582 | - "funding": { | |
| 1583 | - "url": "https://github.com/sponsors/ljharb" | |
| 1584 | - } | |
| 1585 | - }, | |
| 1586 | 1571 | "node_modules/boolbase": { |
| 1587 | 1572 | "version": "1.0.0", |
| 1588 | 1573 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", |
| @@ -1728,6 +1713,7 @@ | ||
| 1728 | 1713 | "version": "2.7.0", |
| 1729 | 1714 | "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", |
| 1730 | 1715 | "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", |
| 1716 | + "license": "MIT", | |
| 1731 | 1717 | "dependencies": { |
| 1732 | 1718 | "follow-redirects": "^1.15.6" |
| 1733 | 1719 | } |
| @@ -2540,9 +2526,9 @@ | ||
| 2540 | 2526 | "license": "MIT" |
| 2541 | 2527 | }, |
| 2542 | 2528 | "node_modules/encodeurl": { |
| 2543 | 2529 | "version": "12.0.20", |
| 2544 | 2530 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-12.0.20.tgz", |
| 2545 | 2531 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0wQ0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", |
| 2546 | 2532 | "license": "MIT", |
| 2547 | 2533 | "engines": { |
| 2548 | 2534 | "node": ">= 0.8" |
| @@ -2879,9 +2865,9 @@ | ||
| 2879 | 2865 | "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" |
| 2880 | 2866 | }, |
| 2881 | 2867 | "node_modules/express": { |
| 2882 | 2868 | "version": "4.2021.0", |
| 2883 | 2869 | "resolved": "https://registry.npmjs.org/express/-/express-4.2021.0.tgz", |
| 2884 | 2870 | "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2VqcNGcj/Id5ZT1LZ/cfihi3ttTn+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLwNJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", |
| 2885 | 2871 | "license": "MIT", |
| 2886 | 2872 | "dependencies": { |
| 2887 | 2873 | "accepts": "~1.3.8", |
| @@ -2896,7 +2882,7 @@ | ||
| 2896 | 2882 | "encodeurl": "~2.0.0", |
| 2897 | 2883 | "escape-html": "~1.0.3", |
| 2898 | 2884 | "etag": "~1.8.1", |
| 2899 | 2885 | "finalhandler": "1.23.01", |
| 2900 | 2886 | "fresh": "0.5.2", |
| 2901 | 2887 | "http-errors": "2.0.0", |
| 2902 | 2888 | "merge-descriptors": "1.0.3", |
| @@ -2905,11 +2891,11 @@ | ||
| 2905 | 2891 | "parseurl": "~1.3.3", |
| 2906 | 2892 | "path-to-regexp": "0.1.10", |
| 2907 | 2893 | "proxy-addr": "~2.0.7", |
| 2908 | 2894 | "qs": "6.1113.0", |
| 2909 | 2895 | "range-parser": "~1.2.1", |
| 2910 | 2896 | "safe-buffer": "5.2.1", |
| 2911 | 2897 | "send": "0.19.0", |
| 2912 | 2898 | "serve-static": "1.16.02", |
| 2913 | 2899 | "setprototypeof": "1.2.0", |
| 2914 | 2900 | "statuses": "2.0.1", |
| 2915 | 2901 | "type-is": "~1.6.18", |
| @@ -2928,15 +2914,6 @@ | ||
| 2928 | 2914 | "node": ">= 0.6" |
| 2929 | 2915 | } |
| 2930 | 2916 | }, |
| 2931 | - "node_modules/express/node_modules/encodeurl": { | |
| 2932 | - "version": "2.0.0", | |
| 2933 | - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", | |
| 2934 | - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", | |
| 2935 | - "license": "MIT", | |
| 2936 | - "engines": { | |
| 2937 | - "node": ">= 0.8" | |
| 2938 | - } | |
| 2939 | - }, | |
| 2940 | 2917 | "node_modules/express/node_modules/safe-buffer": { |
| 2941 | 2918 | "version": "5.2.1", |
| 2942 | 2919 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", |
| @@ -3033,13 +3010,13 @@ | ||
| 3033 | 3010 | } |
| 3034 | 3011 | }, |
| 3035 | 3012 | "node_modules/finalhandler": { |
| 3036 | 3013 | "version": "1.23.01", |
| 3037 | 3014 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.23.01.tgz", |
| 3038 | 3015 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", |
| 3039 | 3016 | "license": "MIT", |
| 3040 | 3017 | "dependencies": { |
| 3041 | 3018 | "debug": "2.6.9", |
| 3042 | 3019 | "encodeurl": "~12.0.20", |
| 3043 | 3020 | "escape-html": "~1.0.3", |
| 3044 | 3021 | "on-finished": "2.4.1", |
| 3045 | 3022 | "parseurl": "~1.3.3", |
| @@ -4080,9 +4057,9 @@ | ||
| 4080 | 4057 | } |
| 4081 | 4058 | }, |
| 4082 | 4059 | "node_modules/load-bmfont": { |
| 4083 | 4060 | "version": "1.4.12", |
| 4084 | 4061 | "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.12.tgz", |
| 4085 | 4062 | "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJqElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/7Enqmwk2sM14soAdmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", |
| 4086 | 4063 | "license": "MIT", |
| 4087 | 4064 | "dependencies": { |
| 4088 | 4065 | "buffer-equal": "0.0.1", |
| @@ -4090,7 +4067,7 @@ | ||
| 4090 | 4067 | "parse-bmfont-ascii": "^1.0.3", |
| 4091 | 4068 | "parse-bmfont-binary": "^1.0.5", |
| 4092 | 4069 | "parse-bmfont-xml": "^1.1.4", |
| 4093 | 4070 | "phin": "^23.97.1", |
| 4094 | 4071 | "xhr": "^2.0.1", |
| 4095 | 4072 | "xtend": "^4.0.0" |
| 4096 | 4073 | } |
| @@ -4692,13 +4669,13 @@ | ||
| 4692 | 4669 | "license": "MIT" |
| 4693 | 4670 | }, |
| 4694 | 4671 | "node_modules/parse-bmfont-xml": { |
| 4695 | 4672 | "version": "1.1.46", |
| 4696 | 4673 | "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.46.tgz", |
| 4697 | 4674 | "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+mCQXuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", |
| 4698 | 4675 | "license": "MIT", |
| 4699 | 4676 | "dependencies": { |
| 4700 | 4677 | "xml-parse-from-string": "^1.0.0", |
| 4701 | 4678 | "xml2js": "^0.4.5.0" |
| 4702 | 4679 | } |
| 4703 | 4680 | }, |
| 4704 | 4681 | "node_modules/parse-headers": { |
| @@ -4814,6 +4791,7 @@ | ||
| 4814 | 4791 | "version": "3.7.1", |
| 4815 | 4792 | "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", |
| 4816 | 4793 | "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", |
| 4794 | + "license": "MIT", | |
| 4817 | 4795 | "dependencies": { |
| 4818 | 4796 | "centra": "^2.7.0" |
| 4819 | 4797 | }, |
| @@ -5032,12 +5010,12 @@ | ||
| 5032 | 5010 | } |
| 5033 | 5011 | }, |
| 5034 | 5012 | "node_modules/qs": { |
| 5035 | 5013 | "version": "6.1113.0", |
| 5036 | 5014 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.1113.0.tgz", |
| 5037 | 5015 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/MaJzRkIk4QXOq7UU2Nx6nsjvS16Z9uwfpg==", |
| 5038 | 5016 | "license": "BSD-3-Clause", |
| 5039 | 5017 | "dependencies": { |
| 5040 | 5018 | "side-channel": "^1.0.46" |
| 5041 | 5019 | }, |
| 5042 | 5020 | "engines": { |
| 5043 | 5021 | "node": ">=0.6" |
| @@ -5344,9 +5322,9 @@ | ||
| 5344 | 5322 | } |
| 5345 | 5323 | }, |
| 5346 | 5324 | "node_modules/sax": { |
| 5347 | 5325 | "version": "1.2.4.1", |
| 5348 | 5326 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.1.tgz", |
| 5349 | 5327 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9+aWOz7yVScEGoKNd4PA10LZ8sk0A/atZwz5+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWwnXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", |
| 5350 | 5328 | "license": "ISC" |
| 5351 | 5329 | }, |
| 5352 | 5330 | "node_modules/send": { |
| @@ -5373,6 +5351,15 @@ | ||
| 5373 | 5351 | "node": ">= 0.8.0" |
| 5374 | 5352 | } |
| 5375 | 5353 | }, |
| 5354 | + "node_modules/send/node_modules/encodeurl": { | |
| 5355 | + "version": "1.0.2", | |
| 5356 | + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", | |
| 5357 | + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", | |
| 5358 | + "license": "MIT", | |
| 5359 | + "engines": { | |
| 5360 | + "node": ">= 0.8" | |
| 5361 | + } | |
| 5362 | + }, | |
| 5376 | 5363 | "node_modules/send/node_modules/ms": { |
| 5377 | 5364 | "version": "2.1.3", |
| 5378 | 5365 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", |
| @@ -5380,15 +5367,15 @@ | ||
| 5380 | 5367 | "license": "MIT" |
| 5381 | 5368 | }, |
| 5382 | 5369 | "node_modules/serve-static": { |
| 5383 | 5370 | "version": "1.16.02", |
| 5384 | 5371 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.02.tgz", |
| 5385 | 5372 | "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4VqpjJZKadQB/hQGdPAPEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", |
| 5386 | 5373 | "license": "MIT", |
| 5387 | 5374 | "dependencies": { |
| 5388 | 5375 | "encodeurl": "~12.0.20", |
| 5389 | 5376 | "escape-html": "~1.0.3", |
| 5390 | 5377 | "parseurl": "~1.3.3", |
| 5391 | 5378 | "send": "0.1819.0" |
| 5392 | 5379 | }, |
| 5393 | 5380 | "engines": { |
| 5394 | 5381 | "node": ">= 0.8.0" |
| @@ -12,7 +12,7 @@ | ||
| 12 | 12 | "cookie-session": "^2.1.0", |
| 13 | 13 | "cors": "^2.8.5", |
| 14 | 14 | "csrf-csrf": "^2.2.3", |
| 15 | 15 | "express": "^4.2021.0", |
| 16 | 16 | "form-data": "^4.0.0", |
| 17 | 17 | "google-translate-api-browser": "^3.0.1", |
| 18 | 18 | "he": "^1.2.0", |
| @@ -49,23 +49,14 @@ | ||
| 49 | 49 | "node": ">= 18" |
| 50 | 50 | }, |
| 51 | 51 | "overrides": { |
| 52 | - "parse-bmfont-xml": { | |
| 53 | - "xml2js": "^0.5.0" | |
| 54 | - }, | |
| 55 | 52 | "vectra": { |
| 56 | 53 | "openai": "^4.17.0" |
| 57 | 54 | }, |
| 58 | - "load-bmfont": { | |
| 59 | - "phin": "^3.7.1" | |
| 60 | - }, | |
| 61 | 55 | "axios": { |
| 62 | 56 | "follow-redirects": "^1.15.4" |
| 63 | 57 | }, |
| 64 | 58 | "node-fetch": { |
| 65 | 59 | "whatwg-url": "^14.0.0" |
| 66 | - }, | |
| 67 | - "express": { | |
| 68 | - "send": "^0.19.0" | |
| 69 | 60 | } |
| 70 | 61 | }, |
| 71 | 62 | "name": "sillytavern", |
| @@ -75,7 +66,7 @@ | ||
| 75 | 66 | "type": "git", |
| 76 | 67 | "url": "https://github.com/SillyTavern/SillyTavern.git" |
| 77 | 68 | }, |
| 78 | 69 | "version": "1.12.56", |
| 79 | 70 | "scripts": { |
| 80 | 71 | "start": "node server.js", |
| 81 | 72 | "start:no-csrf": "node server.js --disableCsrf", |
| @@ -103,7 +103,6 @@ | ||
| 103 | 103 | touch-action: none; |
| 104 | 104 | overflow: hidden; |
| 105 | 105 | position: fixed; |
| 106 | - | |
| 107 | 106 | } |
| 108 | 107 | |
| 109 | 108 | .world_entry_form_control { |
| @@ -112,7 +111,7 @@ | ||
| 112 | 111 | |
| 113 | 112 | .drawer-content { |
| 114 | 113 | min-width: unset; |
| 115 | 114 | width: 100%100dvw; |
| 116 | 115 | max-height: calc(100vh - 45px); |
| 117 | 116 | max-height: calc(100dvh - 45px); |
| 118 | 117 | position: fixed; |
| @@ -159,9 +158,9 @@ | ||
| 159 | 158 | #sheld, |
| 160 | 159 | #character_popup, |
| 161 | 160 | .drawer-content { |
| 162 | 161 | width: 100%100dvw !important; |
| 163 | 162 | margin: 0 auto; |
| 164 | 163 | max-width: 100%100dvw; |
| 165 | 164 | left: 0 !important; |
| 166 | 165 | resize: none !important; |
| 167 | 166 | top: var(--topBarBlockSize); |
| @@ -233,9 +232,9 @@ | ||
| 233 | 232 | /* 100vh are fallback units for browsers that don't support dvh */ |
| 234 | 233 | height: calc(100vh - 45px); |
| 235 | 234 | height: calc(100dvh - 45px); |
| 236 | 235 | min-width: 100%100dvw !important; |
| 237 | 236 | width: 100%100dvw !important; |
| 238 | 237 | max-width: 100%100dvw !important; |
| 239 | 238 | overflow-y: hidden; |
| 240 | 239 | border-left: 1px solid var(--SmartThemeBorderColor); |
| 241 | 240 | border-right: 1px solid var(--SmartThemeBorderColor); |
| @@ -481,10 +480,10 @@ | ||
| 481 | 480 | margin: 0 auto; |
| 482 | 481 | height: calc(100vh - 70px); |
| 483 | 482 | height: calc(100dvh - 70px); |
| 484 | 483 | width: calc(100%100dvw - 5px); |
| 485 | 484 | max-height: calc(100vh - 70px); |
| 486 | 485 | max-height: calc(100dvh - 70px); |
| 487 | 486 | max-width: calc(100%100dvw - 5px); |
| 488 | 487 | |
| 489 | 488 | } |
| 490 | 489 | |
| @@ -47,6 +47,10 @@ dialog { | ||
| 47 | 47 | padding: 1px; |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | +.popup:not(:has(.img_enlarged_container)) .popup-body { | |
| 51 | + max-height: 95dvh; | |
| 52 | +} | |
| 53 | + | |
| 50 | 54 | .popup .popup-content { |
| 51 | 55 | margin-top: 10px; |
| 52 | 56 | padding: 0 8px; |
| @@ -91,15 +91,34 @@ | ||
| 91 | 91 | #completion_prompt_manager_popup #completion_prompt_manager_popup_inspect { |
| 92 | 92 | display: none; |
| 93 | 93 | padding: 0.5em; |
| 94 | + height: 100%; | |
| 95 | + display: flex; | |
| 96 | + flex-direction: column; | |
| 94 | 97 | } |
| 95 | 98 | |
| 96 | 99 | #completion_prompt_manager_popup .completion_prompt_manager_popup_entry { |
| 97 | 100 | padding: 1em0.5em; |
| 98 | 101 | margin-topflex: 2em1; |
| 102 | +} | |
| 103 | + | |
| 104 | +#completion_prompt_manager_popup .completion_prompt_manager_popup_entry_form { | |
| 105 | + height: 100%; | |
| 106 | + display: flex; | |
| 107 | + flex-direction: column; | |
| 108 | +} | |
| 109 | + | |
| 110 | +#completion_prompt_manager_popup .completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt) { | |
| 111 | + flex: 1; | |
| 112 | + display: flex; | |
| 113 | + flex-direction: column; | |
| 114 | +} | |
| 115 | + | |
| 116 | +#completion_prompt_manager_popup #completion_prompt_manager_popup_entry_form_prompt { | |
| 117 | + flex: 1; | |
| 99 | 118 | } |
| 100 | 119 | |
| 101 | 120 | #completion_prompt_manager_popup #completion_prompt_manager_popup_inspect .completion_prompt_manager_popup_entry { |
| 102 | 121 | padding: 1em0.5em; |
| 103 | 122 | } |
| 104 | 123 | |
| 105 | 124 | #completion_prompt_manager_popup #completion_prompt_manager_popup_entry_form_inspect_list { |
| @@ -248,7 +267,7 @@ | ||
| 248 | 267 | } |
| 249 | 268 | |
| 250 | 269 | #completion_prompt_manager_footer_append_prompt { |
| 251 | 270 | font-size: 16px1em; |
| 252 | 271 | } |
| 253 | 272 | |
| 254 | 273 | #prompt-manager-export-format-popup { |
| @@ -303,10 +322,6 @@ | ||
| 303 | 322 | justify-content: space-between; |
| 304 | 323 | } |
| 305 | 324 | |
| 306 | -#prompt-manager-export-format-popup span { | |
| 307 | - font-size: 16px; | |
| 308 | -} | |
| 309 | - | |
| 310 | 325 | @media screen and (max-width: 412px) { |
| 311 | 326 | #completion_prompt_manager_popup { |
| 312 | 327 | max-width: 100%; |
| @@ -316,3 +331,15 @@ | ||
| 316 | 331 | margin-left: 0.5em; |
| 317 | 332 | } |
| 318 | 333 | } |
| 334 | + | |
| 335 | +.completion_prompt_manager_popup_entry_form_control:has(#completion_prompt_manager_popup_entry_form_prompt:disabled)>div:first-child::after { | |
| 336 | + content: 'The content of this prompt is pulled from elsewhere and cannot be edited here.'; | |
| 337 | + display: block; | |
| 338 | + width: 100%; | |
| 339 | + font-weight: 600; | |
| 340 | + text-align: center; | |
| 341 | +} | |
| 342 | + | |
| 343 | +.completion_prompt_manager_popup_entry_form_control #completion_prompt_manager_popup_entry_form_prompt:disabled { | |
| 344 | + visibility: hidden; | |
| 345 | +} | |
| @@ -274,3 +274,7 @@ select.keyselect+span.select2-container .select2-selection--multiple { | ||
| 274 | 274 | align-self: center; |
| 275 | 275 | width: 100%; |
| 276 | 276 | } |
| 277 | + | |
| 278 | +.world_entry:has(input[name="delay_until_recursion"]:not(:checked)) .world_entry_form_control:has(input[name="delayUntilRecursionLevel"]) { | |
| 279 | + display: none; | |
| 280 | +} | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +<svg xmlns="http://www.w3.org/2000/svg" width="10" height="7"> | |
| 2 | + <polygon points="0,0 10,0 5,7" fill="#888888" /> | |
| 3 | +</svg> | |
Binary file
| @@ -119,7 +119,7 @@ | ||
| 119 | 119 | <option value="gui" data-i18n="Default">Default</option> |
| 120 | 120 | </select> |
| 121 | 121 | <div class="flex-container marginLeft5 "> |
| 122 | 122 | <input type="file" hidden data-preset-manager-file="novel" accept=".json, .settings, .preset"> |
| 123 | 123 | <i data-preset-manager-update="novel" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i> |
| 124 | 124 | <i data-preset-manager-new="novel" class="menu_button fa-solid fa-file-circle-plus" title="Save preset as" data-i18n="[title]Save preset as"></i> |
| 125 | 125 | <i data-preset-manager-import="novel" class="menu_button fa-solid fa-file-import" title="Import preset" data-i18n="[title]Import preset"></i> |
| @@ -324,6 +324,19 @@ | ||
| 324 | 324 | </div> |
| 325 | 325 | </div> |
| 326 | 326 | <div class="range-block"> |
| 327 | + <div class="range-block-title" data-i18n="Min P"> | |
| 328 | + Min P | |
| 329 | + </div> | |
| 330 | + <div class="range-block-range-and-counter"> | |
| 331 | + <div class="range-block-range"> | |
| 332 | + <input type="range" id="min_p_novel" name="volume" min="0" max="1" step="0.001"> | |
| 333 | + </div> | |
| 334 | + <div class="range-block-counter"> | |
| 335 | + <input type="number" min="0" max="1" step="0.001" data-for="min_p_novel" id="min_p_counter_novel"> | |
| 336 | + </div> | |
| 337 | + </div> | |
| 338 | + </div> | |
| 339 | + <div class="range-block"> | |
| 327 | 340 | <div class="range-block-title" data-i18n="TFS"> |
| 328 | 341 | TFS |
| 329 | 342 | </div> |
| @@ -337,6 +350,136 @@ | ||
| 337 | 350 | </div> |
| 338 | 351 | </div> |
| 339 | 352 | <div class="range-block"> |
| 353 | + <div class="range-block-title" data-i18n="Top P"> | |
| 354 | + Top P | |
| 355 | + </div> | |
| 356 | + <div class="range-block-range-and-counter"> | |
| 357 | + <div class="range-block-range"> | |
| 358 | + <input type="range" id="top_p_novel" name="volume" min="0" max="1" step="0.001"> | |
| 359 | + </div> | |
| 360 | + <div class="range-block-counter"> | |
| 361 | + <input type="number" min="0" max="1" step="0.001" data-for="top_p_novel" id="top_p_counter_novel"> | |
| 362 | + </div> | |
| 363 | + </div> | |
| 364 | + </div> | |
| 365 | + <div class="range-block"> | |
| 366 | + <div class="range-block-title" data-i18n="Top A"> | |
| 367 | + Top A | |
| 368 | + </div> | |
| 369 | + <div class="range-block-range-and-counter"> | |
| 370 | + <div class="range-block-range"> | |
| 371 | + <input type="range" id="top_a_novel" name="volume" min="0" max="1" step="0.001"> | |
| 372 | + </div> | |
| 373 | + <div class="range-block-counter"> | |
| 374 | + <input type="number" min="0" max="1" step="0.001" data-for="top_a_novel" id="top_a_counter_novel"> | |
| 375 | + </div> | |
| 376 | + </div> | |
| 377 | + </div> | |
| 378 | + <div class="range-block"> | |
| 379 | + <div class="range-block-title" data-i18n="Top K"> | |
| 380 | + Top K | |
| 381 | + </div> | |
| 382 | + <div class="range-block-range-and-counter"> | |
| 383 | + <div class="range-block-range"> | |
| 384 | + <input type="range" id="top_k_novel" name="volume" min="0" max="300" step="1"> | |
| 385 | + </div> | |
| 386 | + <div class="range-block-counter"> | |
| 387 | + <input type="number" min="0" max="300" step="1" data-for="top_k_novel" id="top_k_counter_novel"> | |
| 388 | + </div> | |
| 389 | + </div> | |
| 390 | + </div> | |
| 391 | + <div class="range-block"> | |
| 392 | + <div class="range-block-title" data-i18n="Mirostat Tau"> | |
| 393 | + Mirostat Tau | |
| 394 | + </div> | |
| 395 | + <div class="range-block-range-and-counter"> | |
| 396 | + <div class="range-block-range"> | |
| 397 | + <input type="range" id="mirostat_tau_novel" name="volume" min="0" max="6" step="0.01"> | |
| 398 | + </div> | |
| 399 | + <div class="range-block-counter"> | |
| 400 | + <input type="number" min="0" max="6" step="0.01" data-for="mirostat_tau_novel" id="mirostat_tau_counter_novel"> | |
| 401 | + </div> | |
| 402 | + </div> | |
| 403 | + </div> | |
| 404 | + <div class="range-block"> | |
| 405 | + <div class="range-block-title" data-i18n="Mirostat LR"> | |
| 406 | + Mirostat LR | |
| 407 | + </div> | |
| 408 | + <div class="range-block-range-and-counter"> | |
| 409 | + <div class="range-block-range"> | |
| 410 | + <input type="range" id="mirostat_lr_novel" name="volume" min="0" max="1" step="0.01"> | |
| 411 | + </div> | |
| 412 | + <div class="range-block-counter"> | |
| 413 | + <input type="number" min="0" max="1" step="0.01" data-for="mirostat_lr_novel" id="mirostat_lr_counter_novel"> | |
| 414 | + </div> | |
| 415 | + </div> | |
| 416 | + </div> | |
| 417 | + <div class="range-block"> | |
| 418 | + <div class="range-block-title" data-i18n="Typical P"> | |
| 419 | + Typical P | |
| 420 | + </div> | |
| 421 | + <div class="range-block-range-and-counter"> | |
| 422 | + <div class="range-block-range"> | |
| 423 | + <input type="range" id="typical_p_novel" name="volume" min="0" max="1" step="0.001"> | |
| 424 | + </div> | |
| 425 | + <div class="range-block-counter"> | |
| 426 | + <input type="number" min="0" max="1" step="0.001" data-for="typical_p_novel" id="typical_p_counter_novel"> | |
| 427 | + </div> | |
| 428 | + </div> | |
| 429 | + </div> | |
| 430 | + <div class="range-block"> | |
| 431 | + <div class="range-block-title" data-i18n="Linear"> | |
| 432 | + Linear | |
| 433 | + </div> | |
| 434 | + <div class="range-block-range-and-counter"> | |
| 435 | + <div class="range-block-range"> | |
| 436 | + <input type="range" id="math1_temp_novel" name="volume" min="0" max="1.5" step="0.01"> | |
| 437 | + </div> | |
| 438 | + <div class="range-block-counter"> | |
| 439 | + <input type="number" min="0" max="1.5" step="0.01" data-for="math1_temp_novel" id="math1_temp_counter_novel"> | |
| 440 | + </div> | |
| 441 | + </div> | |
| 442 | + </div> | |
| 443 | + <div class="range-block"> | |
| 444 | + <div class="range-block-title" data-i18n="Quad"> | |
| 445 | + Quad | |
| 446 | + </div> | |
| 447 | + <div class="range-block-range-and-counter"> | |
| 448 | + <div class="range-block-range"> | |
| 449 | + <input type="range" id="math1_quad_novel" name="volume" min="0" max="0.4" step="0.01"> | |
| 450 | + </div> | |
| 451 | + <div class="range-block-counter"> | |
| 452 | + <input type="number" min="0" max="0.4" step="0.01" data-for="math1_quad_novel" id="math1_quad_counter_novel"> | |
| 453 | + </div> | |
| 454 | + </div> | |
| 455 | + </div> | |
| 456 | + <div class="range-block"> | |
| 457 | + <div class="range-block-title" data-i18n="Conf"> | |
| 458 | + Conf | |
| 459 | + </div> | |
| 460 | + <div class="range-block-range-and-counter"> | |
| 461 | + <div class="range-block-range"> | |
| 462 | + <input type="range" id="math1_quad_entropy_scale_novel" name="volume" min="-0.4" max="0.4" step="0.01"> | |
| 463 | + </div> | |
| 464 | + <div class="range-block-counter"> | |
| 465 | + <input type="number" min="-0.4" max="0.4" step="0.01" data-for="math1_quad_entropy_scale_novel" id="math1_quad_entropy_scale_counter_novel"> | |
| 466 | + </div> | |
| 467 | + </div> | |
| 468 | + </div> | |
| 469 | + <div class="range-block"> | |
| 470 | + <div class="range-block-title" data-i18n="Min Length"> | |
| 471 | + Min Length | |
| 472 | + </div> | |
| 473 | + <div class="range-block-range-and-counter"> | |
| 474 | + <div class="range-block-range"> | |
| 475 | + <input type="range" id="min_length_novel" name="volume" min="1" max="150" step="1"> | |
| 476 | + </div> | |
| 477 | + <div class="range-block-counter"> | |
| 478 | + <input type="number" min="1" max="150" step="1" data-for="min_length_novel" id="min_length_counter_novel"> | |
| 479 | + </div> | |
| 480 | + </div> | |
| 481 | + </div> | |
| 482 | + <div class="range-block"> | |
| 340 | 483 | <div class="range-block-title" data-i18n="Phrase Repetition Penalty"> |
| 341 | 484 | Phrase Repetition Penalty |
| 342 | 485 | </div> |
| @@ -349,6 +492,46 @@ | ||
| 349 | 492 | <option value="very_aggressive" data-i18n="Very aggressive">Very aggressive</option> |
| 350 | 493 | </select> |
| 351 | 494 | </div> |
| 495 | + <div class="range-block"> | |
| 496 | + <div class="range-block-title openai_restorable"> | |
| 497 | + <span data-i18n="Preamble">Preamble</span> | |
| 498 | + <div id="nai_preamble_restore" title="Restore default prompt" data-i18n="[title]Restore default prompt" class="right_menu_button"> | |
| 499 | + <div class="fa-solid fa-clock-rotate-left "></div> | |
| 500 | + </div> | |
| 501 | + </div> | |
| 502 | + <div class="toggle-description justifyLeft" data-i18n="Use style tags to modify the writing style of the output."> | |
| 503 | + Use style tags to modify the writing style of the output. | |
| 504 | + </div> | |
| 505 | + <div class="wide100p"> | |
| 506 | + <textarea id="nai_preamble_textarea" class="text_pole textarea_compact" name="nai_preamble" rows="2" placeholder=""></textarea> | |
| 507 | + </div> | |
| 508 | + </div> | |
| 509 | + <div class="range-block"> | |
| 510 | + <div class="range-block-title justifyLeft"> | |
| 511 | + <span data-i18n="Banned Tokens">Banned Tokens</span> | |
| 512 | + </div> | |
| 513 | + <div class="toggle-description justifyLeft" data-i18n="Sequences you don't want to appear in the output. One per line."> | |
| 514 | + Sequences you don't want to appear in the output. One per line. Text or [token ids]. | |
| 515 | + </div> | |
| 516 | + <div class="wide100p"> | |
| 517 | + <textarea id="nai_banned_tokens" class="text_pole textarea_compact" name="nai_banned_tokens" rows="3" placeholder="Example: some text [42, 69, 1337]"></textarea> | |
| 518 | + </div> | |
| 519 | + </div> | |
| 520 | + <div class="range-block"> | |
| 521 | + <div id="logit_bias_novel" class="range-block-title title_restorable"> | |
| 522 | + <span data-i18n="Logit Bias">Logit Bias</span> | |
| 523 | + <div id="novelai_logit_bias_new_entry" class="menu_button menu_button_icon"> | |
| 524 | + <i class="fa-xs fa-solid fa-plus"></i> | |
| 525 | + <small data-i18n="Add">Add</small> | |
| 526 | + </div> | |
| 527 | + </div> | |
| 528 | + <div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words"> | |
| 529 | + Helps to ban or reinforce the usage of certain tokens. | |
| 530 | + </div> | |
| 531 | + <div class="flex-container flexFlowColumn wide100p"> | |
| 532 | + <div class="logit_bias_list"></div> | |
| 533 | + </div> | |
| 534 | + </div> | |
| 352 | 535 | </div> |
| 353 | 536 | <div id="range_block_openai"> |
| 354 | 537 | <div class="range-block"> |
| @@ -907,163 +1090,6 @@ | ||
| 907 | 1090 | </div> |
| 908 | 1091 | </div><!-- end of kobold settings--> |
| 909 | 1092 | <div id="novel_api-settings"> |
| 910 | - <div class="range-block"> | |
| 911 | - <div class="range-block-title openai_restorable"> | |
| 912 | - <span data-i18n="Preamble">Preamble</span> | |
| 913 | - <div id="nai_preamble_restore" title="Restore default prompt" data-i18n="[title]Restore default prompt" class="right_menu_button"> | |
| 914 | - <div class="fa-solid fa-clock-rotate-left "></div> | |
| 915 | - </div> | |
| 916 | - </div> | |
| 917 | - <div class="toggle-description justifyLeft" data-i18n="Use style tags to modify the writing style of the output."> | |
| 918 | - Use style tags to modify the writing style of the output. | |
| 919 | - </div> | |
| 920 | - <div class="wide100p"> | |
| 921 | - <textarea id="nai_preamble_textarea" class="text_pole textarea_compact" name="nai_preamble" rows="2" placeholder=""></textarea> | |
| 922 | - </div> | |
| 923 | - </div> | |
| 924 | - <div class="range-block"> | |
| 925 | - <div class="range-block-title justifyLeft"> | |
| 926 | - <span data-i18n="Banned Tokens">Banned Tokens</span> | |
| 927 | - </div> | |
| 928 | - <div class="toggle-description justifyLeft" data-i18n="Sequences you don't want to appear in the output. One per line."> | |
| 929 | - Sequences you don't want to appear in the output. One per line. Text or [token ids]. | |
| 930 | - </div> | |
| 931 | - <div class="wide100p"> | |
| 932 | - <textarea id="nai_banned_tokens" class="text_pole textarea_compact" name="nai_banned_tokens" rows="3" placeholder="Example: some text [42, 69, 1337]"></textarea> | |
| 933 | - </div> | |
| 934 | - </div> | |
| 935 | - <div class="range-block"> | |
| 936 | - <div id="logit_bias_novel" class="range-block-title title_restorable"> | |
| 937 | - <span data-i18n="Logit Bias">Logit Bias</span> | |
| 938 | - <div id="novelai_logit_bias_new_entry" class="menu_button menu_button_icon"> | |
| 939 | - <i class="fa-xs fa-solid fa-plus"></i> | |
| 940 | - <small data-i18n="Add">Add</small> | |
| 941 | - </div> | |
| 942 | - </div> | |
| 943 | - <div class="toggle-description justifyLeft" data-i18n="Helps to ban or reenforce the usage of certain words"> | |
| 944 | - Helps to ban or reinforce the usage of certain tokens. | |
| 945 | - </div> | |
| 946 | - <div class="flex-container flexFlowColumn wide100p"> | |
| 947 | - <div class="logit_bias_list"></div> | |
| 948 | - </div> | |
| 949 | - </div> | |
| 950 | - <div class="range-block"> | |
| 951 | - <hr> | |
| 952 | - <div class="range-block-title justifyLeft" data-i18n="CFG Scale"> | |
| 953 | - CFG Scale | |
| 954 | - </div> | |
| 955 | - <div class="range-block-range-and-counter"> | |
| 956 | - <div class="range-block-range"> | |
| 957 | - <input type="range" id="cfg_scale_novel" name="volume" min="1" max="3" step="0.05"> | |
| 958 | - </div> | |
| 959 | - <div class="range-block-counter"> | |
| 960 | - <input type="number" min="1" max="3" step="0.05" data-for="cfg_scale_novel" id="cfg_scale_counter_novel"> | |
| 961 | - </div> | |
| 962 | - </div> | |
| 963 | - </div> | |
| 964 | - <div class="range-block"> | |
| 965 | - <div class="range-block-title justifyLeft"> | |
| 966 | - <span data-i18n="Negative Prompt">Negative Prompt</span> | |
| 967 | - </div> | |
| 968 | - <div class="wide100p"> | |
| 969 | - <textarea id="nai_cfg_uc" class="text_pole textarea_compact" name="cfg_uc" rows="2" data-i18n="[placeholder]Add text here that would make the AI generate things you don't want in your outputs." placeholder="Add text here that would make the AI generate things you don't want in your outputs."></textarea> | |
| 970 | - </div> | |
| 971 | - <small class="margin-bot-10px" data-i18n="Used if CFG Scale is unset globally, per chat or character"> | |
| 972 | - Used if CFG Scale is unset globally, per chat or character | |
| 973 | - </small> | |
| 974 | - <hr> | |
| 975 | - </div> | |
| 976 | - <div class="range-block"> | |
| 977 | - <div class="range-block-title" data-i18n="Top P"> | |
| 978 | - Top P | |
| 979 | - </div> | |
| 980 | - <div class="range-block-range-and-counter"> | |
| 981 | - <div class="range-block-range"> | |
| 982 | - <input type="range" id="top_p_novel" name="volume" min="0" max="1" step="0.001"> | |
| 983 | - </div> | |
| 984 | - <div class="range-block-counter"> | |
| 985 | - <input type="number" min="0" max="1" step="0.001" data-for="top_p_novel" id="top_p_counter_novel"> | |
| 986 | - </div> | |
| 987 | - </div> | |
| 988 | - </div> | |
| 989 | - <div class="range-block"> | |
| 990 | - <div class="range-block-title" data-i18n="Top A"> | |
| 991 | - Top A | |
| 992 | - </div> | |
| 993 | - <div class="range-block-range-and-counter"> | |
| 994 | - <div class="range-block-range"> | |
| 995 | - <input type="range" id="top_a_novel" name="volume" min="0" max="1" step="0.001"> | |
| 996 | - </div> | |
| 997 | - <div class="range-block-counter"> | |
| 998 | - <input type="number" min="0" max="1" step="0.001" data-for="top_a_novel" id="top_a_counter_novel"> | |
| 999 | - </div> | |
| 1000 | - </div> | |
| 1001 | - </div> | |
| 1002 | - <div class="range-block"> | |
| 1003 | - <div class="range-block-title" data-i18n="Top K"> | |
| 1004 | - Top K | |
| 1005 | - </div> | |
| 1006 | - <div class="range-block-range-and-counter"> | |
| 1007 | - <div class="range-block-range"> | |
| 1008 | - <input type="range" id="top_k_novel" name="volume" min="0" max="300" step="1"> | |
| 1009 | - </div> | |
| 1010 | - <div class="range-block-counter"> | |
| 1011 | - <input type="number" min="0" max="300" step="1" data-for="top_k_novel" id="top_k_counter_novel"> | |
| 1012 | - </div> | |
| 1013 | - </div> | |
| 1014 | - </div> | |
| 1015 | - <div class="range-block"> | |
| 1016 | - <div class="range-block-title" data-i18n="Mirostat Tau"> | |
| 1017 | - Mirostat Tau | |
| 1018 | - </div> | |
| 1019 | - <div class="range-block-range-and-counter"> | |
| 1020 | - <div class="range-block-range"> | |
| 1021 | - <input type="range" id="mirostat_tau_novel" name="volume" min="0" max="6" step="0.01"> | |
| 1022 | - </div> | |
| 1023 | - <div class="range-block-counter"> | |
| 1024 | - <input type="number" min="0" max="6" step="0.01" data-for="mirostat_tau_novel" id="mirostat_tau_counter_novel"> | |
| 1025 | - </div> | |
| 1026 | - </div> | |
| 1027 | - </div> | |
| 1028 | - <div class="range-block"> | |
| 1029 | - <div class="range-block-title" data-i18n="Mirostat LR"> | |
| 1030 | - Mirostat LR | |
| 1031 | - </div> | |
| 1032 | - <div class="range-block-range-and-counter"> | |
| 1033 | - <div class="range-block-range"> | |
| 1034 | - <input type="range" id="mirostat_lr_novel" name="volume" min="0" max="1" step="0.01"> | |
| 1035 | - </div> | |
| 1036 | - <div class="range-block-counter"> | |
| 1037 | - <input type="number" min="0" max="1" step="0.01" data-for="mirostat_lr_novel" id="mirostat_lr_counter_novel"> | |
| 1038 | - </div> | |
| 1039 | - </div> | |
| 1040 | - </div> | |
| 1041 | - <div class="range-block"> | |
| 1042 | - <div class="range-block-title" data-i18n="Typical P"> | |
| 1043 | - Typical P | |
| 1044 | - </div> | |
| 1045 | - <div class="range-block-range-and-counter"> | |
| 1046 | - <div class="range-block-range"> | |
| 1047 | - <input type="range" id="typical_p_novel" name="volume" min="0" max="1" step="0.001"> | |
| 1048 | - </div> | |
| 1049 | - <div class="range-block-counter"> | |
| 1050 | - <input type="number" min="0" max="1" step="0.001" data-for="typical_p_novel" id="typical_p_counter_novel"> | |
| 1051 | - </div> | |
| 1052 | - </div> | |
| 1053 | - </div> | |
| 1054 | - <div class="range-block"> | |
| 1055 | - <div class="range-block-title" data-i18n="Min Length"> | |
| 1056 | - Min Length | |
| 1057 | - </div> | |
| 1058 | - <div class="range-block-range-and-counter"> | |
| 1059 | - <div class="range-block-range"> | |
| 1060 | - <input type="range" id="min_length_novel" name="volume" min="1" max="150" step="1"> | |
| 1061 | - </div> | |
| 1062 | - <div class="range-block-counter"> | |
| 1063 | - <input type="number" min="1" max="150" step="1" data-for="min_length_novel" id="min_length_counter_novel"> | |
| 1064 | - </div> | |
| 1065 | - </div> | |
| 1066 | - </div> | |
| 1067 | 1093 | <div class="range-block flexFlowColumn"> |
| 1068 | 1094 | <div class="range-block-title"> |
| 1069 | 1095 | <span data-i18n="Samplers Order">Samplers Order</span> |
| @@ -1102,17 +1128,22 @@ | ||
| 1102 | 1128 | <small>5</small> |
| 1103 | 1129 | <div class="toggle_button right_menu_button"></div> |
| 1104 | 1130 | </div> |
| 1105 | - <div data-id="6"> | |
| 1131 | + <!-- 6 and 7 were removed samplers. --> | |
| 1106 | - <span data-i18n="CFG">CFG</span> | |
| 1107 | - <small>6</small> | |
| 1108 | - <div class="toggle_button right_menu_button"></div> | |
| 1109 | - </div> | |
| 1110 | - <!-- Yes, there is no 7. It was a removed Top G sampling. RIP. --> | |
| 1111 | 1132 | <div data-id="8"> |
| 1112 | 1133 | <span data-i18n="Mirostat">Mirostat</span> |
| 1113 | 1134 | <small>8</small> |
| 1114 | 1135 | <div class="toggle_button right_menu_button"></div> |
| 1115 | 1136 | </div> |
| 1137 | + <div data-id="9"> | |
| 1138 | + <span data-i18n="Unified Sampling">Unified Sampling</span> | |
| 1139 | + <small>9</small> | |
| 1140 | + <div class="toggle_button right_menu_button"></div> | |
| 1141 | + </div> | |
| 1142 | + <div data-id="10"> | |
| 1143 | + <span data-i18n="Min P">Min P</span> | |
| 1144 | + <small>10</small> | |
| 1145 | + <div class="toggle_button right_menu_button"></div> | |
| 1146 | + </div> | |
| 1116 | 1147 | </div> |
| 1117 | 1148 | </div> |
| 1118 | 1149 | </div><!-- end of novel settings--> |
| @@ -1279,7 +1310,7 @@ | ||
| 1279 | 1310 | </div> |
| 1280 | 1311 | </div> |
| 1281 | 1312 | |
| 1282 | 1313 | <div data-tg-type="koboldcpp, aphrodite" id="xtc_block" class="wide100p"> |
| 1283 | 1314 | <h4 class="wide100p textAlignCenter"> |
| 1284 | 1315 | <label data-i18n="Exclude Top Choices (XTC)">Exclude Top Choices (XTC)</label> |
| 1285 | 1316 | <a href="https://github.com/oobabooga/text-generation-webui/pull/6335" target="_blank"> |
| @@ -1339,7 +1370,7 @@ | ||
| 1339 | 1370 | </div> |
| 1340 | 1371 | </div> |
| 1341 | 1372 | </div> |
| 1342 | 1373 | <div data-tg-type="ooba, mancer, koboldcpp, tabby, llamacpp, aphrodite" id="dynatemp_block_ooba" class="wide100p"> |
| 1343 | 1374 | <h4 class="wide100p textAlignCenter"> |
| 1344 | 1375 | <div class="flex-container alignitemscenter justifyCenter"> |
| 1345 | 1376 | <div class="checkbox_label" for="dynatemp_textgenerationwebui"> |
| @@ -2038,6 +2069,7 @@ | ||
| 2038 | 2069 | <select id="model_novel_select"> |
| 2039 | 2070 | <option value="clio-v1">Clio</option> |
| 2040 | 2071 | <option value="kayra-v1">Kayra</option> |
| 2072 | + <option value="llama-3-erato-v1">Erato</option> | |
| 2041 | 2073 | </select> |
| 2042 | 2074 | <div class="flex-container"> |
| 2043 | 2075 | <div id="api_button_novel" class="api_button menu_button menu_button_icon" type="submit" data-i18n="Connect">Connect</div> |
| @@ -2850,10 +2882,13 @@ | ||
| 2850 | 2882 | <option value="gemini-1.5-pro-exp-0827">Gemini 1.5 Pro Experiment 2024-08-27</option> |
| 2851 | 2883 | <option value="gemini-1.5-pro-latest">Gemini 1.5 Pro [latest]</option> |
| 2852 | 2884 | <option value="gemini-1.5-pro-001">Gemini 1.5 Pro [001]</option> |
| 2885 | + <option value="gemini-1.5-pro-002">Gemini 1.5 Pro [002]</option> | |
| 2853 | 2886 | <option value="gemini-1.5-flash-exp-0827">Gemini 1.5 Flash Experiment 2024-08-27</option> |
| 2854 | 2887 | <option value="gemini-1.5-flash-8b-exp-0827">Gemini 1.5 Flash 8B Experiment 2024-08-27</option> |
| 2888 | + <option value="gemini-1.5-flash-8b-exp-0924">Gemini 1.5 Flash 8B Experiment 2024-09-24</option> | |
| 2855 | 2889 | <option value="gemini-1.5-flash-latest">Gemini 1.5 Flash [latest]</option> |
| 2856 | 2890 | <option value="gemini-1.5-flash-001">Gemini 1.5 Flash [001]</option> |
| 2891 | + <option value="gemini-1.5-flash-002">Gemini 1.5 Flash [002]</option> | |
| 2857 | 2892 | <option value="gemini-1.0-pro-latest">Gemini 1.0 Pro [latest]</option> |
| 2858 | 2893 | <option value="gemini-1.0-pro-001">Gemini 1.0 Pro (Tuning) [001]</option> |
| 2859 | 2894 | <option value="gemini-1.0-pro-vision-latest">Gemini 1.0 Pro Vision [latest]</option> |
| @@ -2884,6 +2919,7 @@ | ||
| 2884 | 2919 | <option value="mistral-large-latest">mistral-large-latest</option> |
| 2885 | 2920 | <option value="codestral-latest">codestral-latest</option> |
| 2886 | 2921 | <option value="codestral-mamba-latest">codestral-mamba-latest</option> |
| 2922 | + <option value="pixtral-latest">pixtral-latest</option> | |
| 2887 | 2923 | </optgroup> |
| 2888 | 2924 | <optgroup label="Sub-versions"> |
| 2889 | 2925 | <option value="open-mistral-nemo-2407">open-mistral-nemo-2407</option> |
| @@ -2891,11 +2927,13 @@ | ||
| 2891 | 2927 | <option value="mistral-tiny-2312">mistral-tiny-2312</option> |
| 2892 | 2928 | <option value="mistral-small-2312">mistral-small-2312</option> |
| 2893 | 2929 | <option value="mistral-small-2402">mistral-small-2402</option> |
| 2930 | + <option value="mistral-small-2409">mistral-small-2409</option> | |
| 2894 | 2931 | <option value="mistral-medium-2312">mistral-medium-2312</option> |
| 2895 | 2932 | <option value="mistral-large-2402">mistral-large-2402</option> |
| 2896 | 2933 | <option value="mistral-large-2407">mistral-large-2407</option> |
| 2897 | 2934 | <option value="codestral-2405">codestral-2405</option> |
| 2898 | 2935 | <option value="codestral-mamba-2407">codestral-mamba-2407</option> |
| 2936 | + <option value="pixtral-12b-2409">pixtral-12b-2409</option> | |
| 2899 | 2937 | </optgroup> |
| 2900 | 2938 | </select> |
| 2901 | 2939 | </div> |
| @@ -3085,9 +3123,22 @@ | ||
| 3085 | 3123 | <div class="drawer-icon fa-solid fa-font fa-fw closedIcon" title="AI Response Formatting" data-i18n="[title]AI Response Formatting"></div> |
| 3086 | 3124 | </div> |
| 3087 | 3125 | <div id="AdvancedFormatting" class="drawer-content"> |
| 3088 | 3126 | <h3div class="margin0" dataflex-i18n="Advancedcontainer FormattingalignItemsBaseline"> |
| 3127 | + <h3 class="margin0 flex1" data-i18n="Advanced Formatting"> | |
| 3089 | 3128 | Advanced Formatting |
| 3090 | 3129 | </h3> |
| 3130 | + <div class="flex-container"> | |
| 3131 | + <input id="af_master_import_file" type="file" hidden accept=".json" class="displayNone"> | |
| 3132 | + <div id="af_master_import" class="menu_button menu_button_icon" title="Import Advanced Formatting settings You can also provide legacy files for Instruct and Context templates." data-i18n="[title]Import Advanced Formatting settings"> | |
| 3133 | + <i class="fa-solid fa-file-import"></i> | |
| 3134 | + <span data-i18n="Master Import">Master Import</span> | |
| 3135 | + </div> | |
| 3136 | + <div id="af_master_export" class="menu_button menu_button_icon" title="Export Advanced Formatting settings" data-i18n="[title]Export Advanced Formatting settings"> | |
| 3137 | + <i class="fa-solid fa-file-export"></i> | |
| 3138 | + <span data-i18n="Master Export">Master Export</span> | |
| 3139 | + </div> | |
| 3140 | + </div> | |
| 3141 | + </div> | |
| 3091 | 3142 | <div class="flex-container spaceEvenly"> |
| 3092 | 3143 | <div id="ContextSettings" class="flex-container flexNoGap flexFlowColumn flex1"> |
| 3093 | 3144 | <div> |
| @@ -3099,18 +3150,17 @@ | ||
| 3099 | 3150 | </a> |
| 3100 | 3151 | </div> |
| 3101 | 3152 | </h4> |
| 3153 | + <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template"> | |
| 3154 | + <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select> | |
| 3102 | 3155 | <div class="flex-container justifyCenter"> |
| 3103 | 3156 | <input type="file" hidden data-preset-manager-file="context" accept=".json, .settings"> |
| 3104 | - <i id="context_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this template for Instruct Mode." data-i18n="[title]Auto-select this template for Instruct Mode"></i> | |
| 3105 | 3157 | <i data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i> |
| 3106 | 3158 | <i data-preset-manager-new="context" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i> |
| 3107 | 3159 | <i data-preset-manager-import="context" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i> |
| 3108 | 3160 | <i data-preset-manager-export="context" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i> |
| 3109 | 3161 | <i data-preset-manager-restore="context" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i> |
| 3110 | 3162 | <i id="context_delete_preset" data-preset-manager-delete="context" class="menu_button fa-solid fa-trash-can" title="Delete the template" data-i18n="[title]Delete the template"></i> |
| 3111 | 3163 | </div> |
| 3112 | - <div class="flex-container" title="Select your current Context Template" data-i18n="[title]Select your current Context Template"> | |
| 3113 | - <select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select> | |
| 3114 | 3164 | </div> |
| 3115 | 3165 | <div> |
| 3116 | 3166 | <label for="context_story_string" class="flex-container"> |
| @@ -3141,9 +3191,6 @@ | ||
| 3141 | 3191 | <span data-i18n="Context Formatting"> |
| 3142 | 3192 | Context Formatting |
| 3143 | 3193 | </span> |
| 3144 | - <small data-i18n="(Saves to Template)"> | |
| 3145 | - (Saves to Template) | |
| 3146 | - </small> | |
| 3147 | 3194 | </h4> |
| 3148 | 3195 | |
| 3149 | 3196 | <label class="checkbox_label" for="always-force-name2-checkbox"> |
| @@ -3174,49 +3221,19 @@ | ||
| 3174 | 3221 | Trim Incomplete Sentences |
| 3175 | 3222 | </small> |
| 3176 | 3223 | </label> |
| 3177 | - <!-- Add margin since this is a child of above --> | |
| 3178 | - <label class="checkbox_label indent20p" for="include_newline_checkbox"> | |
| 3179 | - <input id="include_newline_checkbox" type="checkbox" /> | |
| 3180 | - <small data-i18n="Include Newline">Include Newline</small> | |
| 3181 | - </label> | |
| 3182 | 3224 | <label class="checkbox_label" title="Add Chat Start and Example Separator to a list of stopping strings." data-i18n="[title]Add Chat Start and Example Separator to a list of stopping strings."> |
| 3183 | 3225 | <input id="context_use_stop_strings" type="checkbox" /> |
| 3184 | 3226 | <small data-i18n="Separators as Stop Strings">Separators as Stop Strings</small> |
| 3185 | 3227 | </label> |
| 3186 | 3228 | <label class="checkbox_label" title="Add Character and User names to a list of stopping strings." data-i18n="[title]Add Character and User names to a list of stopping strings."> |
| 3187 | 3229 | <input id="context_names_as_stop_strings" type="checkbox" /> |
| 3188 | 3230 | <small data-i18n="Names as Stop Strings">Names as Stop Strings</small> |
| 3189 | 3231 | </label> |
| 3190 | 3232 | </div> |
| 3191 | 3233 | |
| 3192 | 3234 | <label class="checkbox_label" title="Includes Post-History Instructions at the end of the prompt, if defined in the character card AND ''Prefer Char. Instructions'' is enabled. THIS IS NOT RECOMMENDED FOR TEXT COMPLETION MODELS, CAN LEAD TO BAD OUTPUT." data-i18n="[title]context_allow_post_history_instructions"> |
| 3193 | 3235 | <input id="context_allow_jailbreak" type="checkbox" /> |
| 3194 | 3236 | <small data-i18n="Allow Post-History Instructions">Allow Post-History Instructions</small> |
| 3195 | - </label> | |
| 3196 | - </div> | |
| 3197 | - <div> | |
| 3198 | - <h4 class="range-block-title justifyLeft standoutHeader"> | |
| 3199 | - <span data-i18n="Custom Stopping Strings"> | |
| 3200 | - Custom Stopping Strings | |
| 3201 | - </span> | |
| 3202 | - <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank"> | |
| 3203 | - <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3204 | - </a> | |
| 3205 | - </h4> | |
| 3206 | - <div> | |
| 3207 | - <small> | |
| 3208 | - <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span> | |
| 3209 | - <i class="fa-solid fa-question-circle opacity50p" title="e.g: ["Ford", "BMW", "Fiat"]"></i> | |
| 3210 | - </small> | |
| 3211 | - </div> | |
| 3212 | - <div> | |
| 3213 | - <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea> | |
| 3214 | - </div> | |
| 3215 | - <label class="checkbox_label" for="custom_stopping_strings_macro"> | |
| 3216 | - <input id="custom_stopping_strings_macro" type="checkbox" checked> | |
| 3217 | - <small data-i18n="Replace Macro in Custom Stopping Strings"> | |
| 3218 | - Replace Macro in Custom Stopping Strings | |
| 3219 | - </small> | |
| 3220 | 3237 | </label> |
| 3221 | 3238 | </div> |
| 3222 | 3239 | </div> |
| @@ -3231,29 +3248,28 @@ | ||
| 3231 | 3248 | </a> |
| 3232 | 3249 | </div> |
| 3233 | 3250 | <div class="flex-container"> |
| 3234 | - <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled"> | |
| 3235 | - <input id="instruct_enabled" type="checkbox" style="display:none;" /> | |
| 3236 | - <small><i class="fa-solid fa-power-off menu_button margin0"></i></small> | |
| 3237 | - </label> | |
| 3238 | 3251 | <label for="instruct_bind_to_context" class="checkbox_label flex1" title="Bind to Context
If enabled, Context templates will be automatically selected based on selected Instruct template name or by preference." data-i18n="[title]instruct_bind_to_context"> |
| 3239 | 3252 | <input id="instruct_bind_to_context" type="checkbox" style="display:none;" /> |
| 3240 | 3253 | <small><i class="fa-solid fa-link menu_button margin0"></i></small> |
| 3241 | 3254 | </label> |
| 3255 | + <label for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled"> | |
| 3256 | + <input id="instruct_enabled" type="checkbox" style="display:none;" /> | |
| 3257 | + <small><i class="fa-solid fa-power-off menu_button margin0"></i></small> | |
| 3258 | + </label> | |
| 3242 | 3259 | </div> |
| 3243 | 3260 | </h4> |
| 3261 | + | |
| 3262 | + <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template"> | |
| 3263 | + <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select> | |
| 3244 | 3264 | <div class="flex-container margin0 justifyCenter"> |
| 3245 | 3265 | <input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings"> |
| 3246 | - <i id="instruct_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this template on API connection." data-i18n="[title]Auto-select this template on API connection"></i> | |
| 3247 | 3266 | <i data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current template" data-i18n="[title]Update current template"></i> |
| 3248 | 3267 | <i data-preset-manager-new="instruct" class="menu_button fa-solid fa-file-circle-plus" title="Save template as" data-i18n="[title]Save template as"></i> |
| 3249 | 3268 | <i data-preset-manager-import="instruct" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i> |
| 3250 | 3269 | <i data-preset-manager-export="instruct" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i> |
| 3251 | 3270 | <i data-preset-manager-restore="instruct" class="menu_button fa-solid fa-recycle" title="Restore current template" data-i18n="[title]Restore current template"></i> |
| 3252 | 3271 | <i data-preset-manager-delete="instruct" class="menu_button fa-solid fa-trash-can" title="Delete template" data-i18n="[title]Delete template"></i> |
| 3253 | 3272 | </div> |
| 3254 | - | |
| 3255 | - <div class="flex-container" title="Select your current Instruct Template" data-i18n="[title]Select your current Instruct Template"> | |
| 3256 | - <select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select> | |
| 3257 | 3273 | </div> |
| 3258 | 3274 | <label> |
| 3259 | 3275 | <small> |
| @@ -3265,14 +3281,6 @@ | ||
| 3265 | 3281 | <input type="text" id="instruct_activation_regex" class="text_pole textarea_compact" placeholder="e.g. /llama(-)?[3|3.1]/i"> |
| 3266 | 3282 | </div> |
| 3267 | 3283 | <div> |
| 3268 | - <label for="instruct_system_prompt" class="flex-container"> | |
| 3269 | - <small data-i18n="System Prompt">System Prompt</small> | |
| 3270 | - <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="instruct_system_prompt" title="Expand the editor" data-i18n="[title]Expand the editor"></i> | |
| 3271 | - </label> | |
| 3272 | - <textarea id="instruct_system_prompt" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3273 | - </div> | |
| 3274 | - | |
| 3275 | - <div> | |
| 3276 | 3284 | <label for="instruct_wrap" class="checkbox_label"> |
| 3277 | 3285 | <input id="instruct_wrap" type="checkbox" /> |
| 3278 | 3286 | <small data-i18n="Wrap Sequences with Newline">Wrap Sequences with Newline</small> |
| @@ -3285,116 +3293,29 @@ | ||
| 3285 | 3293 | <input id="instruct_skip_examples" type="checkbox" /> |
| 3286 | 3294 | <small data-i18n="Skip Example Dialogues Formatting">Skip Example Dialogues Formatting</small> |
| 3287 | 3295 | </label> |
| 3288 | - <label for="instruct_names" class="checkbox_label"> | |
| 3289 | - <input id="instruct_names" type="checkbox" /> | |
| 3290 | - <small data-i18n="Include Names">Include Names</small> | |
| 3291 | - </label> | |
| 3292 | - <label for="instruct_names_force_groups" class="checkbox_label indent20p"> | |
| 3293 | - <input id="instruct_names_force_groups" type="checkbox" /> | |
| 3294 | - <small data-i18n="Force for Groups and Personas">Force for Groups and Personas</small> | |
| 3295 | - </label> | |
| 3296 | - </div> | |
| 3297 | - </div> | |
| 3298 | - <div name="tokenizerSettingsBlock"> | |
| 3299 | - <div name="tokenizerSelectorBlock"> | |
| 3300 | - <h4 class="standoutHeader"><span data-i18n="Tokenizer">Tokenizer</span> | |
| 3301 | - <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#tokenizer" class="notes-link" target="_blank"> | |
| 3302 | - <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3303 | - </a> | |
| 3304 | - </h4> | |
| 3305 | - <select id="tokenizer"> | |
| 3306 | - <option value="99">Best match (recommended)</option> | |
| 3307 | - <option value="0">None / Estimated</option> | |
| 3308 | - <option value="1">GPT-2</option> | |
| 3309 | - <!-- Option #2 was a legacy GPT-2/3 tokenizer --> | |
| 3310 | - <option value="3">Llama 1/2</option> | |
| 3311 | - <option value="12">Llama 3</option> | |
| 3312 | - <option value="13">Gemma / Gemini</option> | |
| 3313 | - <option value="14">Jamba</option> | |
| 3314 | - <option value="15">Qwen2</option> | |
| 3315 | - <option value="16">Command-R</option> | |
| 3316 | - <option value="4">NerdStash (NovelAI Clio)</option> | |
| 3317 | - <option value="5">NerdStash v2 (NovelAI Kayra)</option> | |
| 3318 | - <option value="7">Mistral</option> | |
| 3319 | - <option value="8">Yi</option> | |
| 3320 | - <option value="11">Claude 1/2</option> | |
| 3321 | - <option value="6">API (WebUI / koboldcpp)</option> | |
| 3322 | - </select> | |
| 3323 | - </div> | |
| 3324 | - <div class="range-block flex-container flexnowrap" name="tokenPaddingBlock"> | |
| 3325 | - <div class="range-block-title justifyLeft"> | |
| 3326 | - <small data-i18n="Token Padding" class="flex-container">Token Padding | |
| 3327 | - <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#token-padding" class="notes-link" target="_blank"> | |
| 3328 | - <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3329 | - </a> | |
| 3330 | - </small> | |
| 3331 | - </div> | |
| 3332 | - <input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" /> | |
| 3333 | - </div> | |
| 3334 | - </div> | |
| 3335 | - <div> | |
| 3336 | - <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> | |
| 3337 | - <div> | |
| 3338 | - <small> | |
| 3339 | - <span data-i18n="Non-markdown strings"> | |
| 3340 | - Non-markdown strings | |
| 3341 | - </span> | |
| 3342 | - </small> | |
| 3343 | - <div> | |
| 3344 | - <input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]comma delimited,no spaces between" placeholder="comma delimited,no spaces between" /> | |
| 3345 | - </div> | |
| 3346 | - </div> | |
| 3347 | - | |
| 3348 | - <div name="startReplyWithBlock"> | |
| 3349 | - <div> | |
| 3350 | - <small> | |
| 3351 | - <span data-i18n="Start Reply With"> | |
| 3352 | - Start Reply With | |
| 3353 | - </span> | |
| 3354 | - </small> | |
| 3355 | 3296 | <div> |
| 3356 | - <textarea id="start_reply_with" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3297 | + <small data-i18n="Include Names"> | |
| 3357 | - </div> | |
| 3298 | + Include Names | |
| 3358 | - <label class="checkbox_label" for="chat-show-reply-prefix-checkbox"> | |
| 3359 | - <input id="chat-show-reply-prefix-checkbox" type="checkbox" /> | |
| 3360 | - <small data-i18n="Show reply prefix in chat"> | |
| 3361 | - Show reply prefix in chat | |
| 3362 | 3299 | </small> |
| 3363 | - </label> | |
| 3300 | + <select id="instruct_names_behavior"> | |
| 3364 | - </div> | |
| 3301 | + <option value="none" data-i18n="Never">Never</option> | |
| 3302 | + <option value="force" data-i18n="Groups and Past Personas">Groups and Past Personas</option> | |
| 3303 | + <option value="always" data-i18n="Always">Always</option> | |
| 3304 | + </select> | |
| 3365 | 3305 | </div> |
| 3366 | 3306 | </div> |
| 3367 | 3307 | </div> |
| 3368 | 3308 | <div id="InstructSequencesColumn" class="flex-container flexNoGapwide100p flexFlowColumn flex1"> |
| 3369 | - <div class="wide100p flexFlowColumn"> | |
| 3370 | 3309 | <h4 class="standoutHeader title_restorable"> |
| 3371 | 3310 | <b> |
| 3372 | 3311 | <span data-i18n="Instruct Sequences"> |
| 3373 | 3312 | Instruct Sequences |
| 3374 | 3313 | </span> |
| 3375 | - <small data-i18n="(Saves to Template)"> | |
| 3376 | - (Saves to Template) | |
| 3377 | - </small> | |
| 3378 | 3314 | </b> |
| 3379 | 3315 | </h4> |
| 3380 | - <div class="flex-container"> | |
| 3316 | + <!-- We keep one auto-open so the user would know what is going on in the picked template --> | |
| 3381 | - <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt."> | |
| 3317 | + <details open> | |
| 3382 | - <label for="instruct_system_sequence_prefix"> | |
| 3318 | + <summary>User Message Sequences</summary> | |
| 3383 | - <small data-i18n="System Prompt Prefix">System Prompt Prefix</small> | |
| 3384 | - </label> | |
| 3385 | - <div> | |
| 3386 | - <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3387 | - </div> | |
| 3388 | - </div> | |
| 3389 | - <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt."> | |
| 3390 | - <label for="instruct_system_sequence_suffix"> | |
| 3391 | - <small data-i18n="System Prompt Suffix">System Prompt Suffix</small> | |
| 3392 | - </label> | |
| 3393 | - <div> | |
| 3394 | - <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea> | |
| 3395 | - </div> | |
| 3396 | - </div> | |
| 3397 | - </div> | |
| 3398 | 3319 | <div class="flex-container"> |
| 3399 | 3320 | <div class="flexAuto" title="Inserted before a User message and as a last prompt line when impersonating." data-i18n="[title]Inserted before a User message and as a last prompt line when impersonating."> |
| 3400 | 3321 | <small data-i18n="User Prefix">User Message Prefix</small> |
| @@ -3405,6 +3326,9 @@ | ||
| 3405 | 3326 | <textarea id="instruct_input_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea> |
| 3406 | 3327 | </div> |
| 3407 | 3328 | </div> |
| 3329 | + </details> | |
| 3330 | + <details open> | |
| 3331 | + <summary>Assistant Message Sequences</summary> | |
| 3408 | 3332 | <div class="flex-container"> |
| 3409 | 3333 | <div class="flexAuto" title="Inserted before an Assistant message and as a last prompt line when generating an AI reply." data-i18n="[title]Inserted before an Assistant message and as a last prompt line when generating an AI reply."> |
| 3410 | 3334 | <small data-i18n="Assistant Prefix">Assistant Message Prefix</small> |
| @@ -3415,6 +3339,9 @@ | ||
| 3415 | 3339 | <textarea id="instruct_output_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea> |
| 3416 | 3340 | </div> |
| 3417 | 3341 | </div> |
| 3342 | + </details> | |
| 3343 | + <details> | |
| 3344 | + <summary>System Message Sequences</summary> | |
| 3418 | 3345 | <div class="flex-container"> |
| 3419 | 3346 | <div class="flexAuto" id="instruct_system_sequence_block" title="Inserted before a System (added by slash commands or extensions) message." data-i18n="[title]Inserted before a System (added by slash commands or extensions) message."> |
| 3420 | 3347 | <small data-i18n="System Prefix">System Message Prefix</small> |
| @@ -3431,10 +3358,30 @@ | ||
| 3431 | 3358 | <small data-i18n="System same as User">System same as User</small> |
| 3432 | 3359 | </label> |
| 3433 | 3360 | </div> |
| 3434 | - | |
| 3361 | + </details> | |
| 3435 | - <h5 class="textAlignCenter" data-i18n="Misc. Sequences"> | |
| 3362 | + <details> | |
| 3436 | - Misc. Sequences | |
| 3363 | + <summary>System Prompt Sequences</summary> | |
| 3437 | - </h5> | |
| 3364 | + <div class="flex-container"> | |
| 3365 | + <div class="flexAuto" title="Inserted before a System prompt." data-i18n="[title]Inserted before a System prompt."> | |
| 3366 | + <label for="instruct_system_sequence_prefix"> | |
| 3367 | + <small data-i18n="System Prompt Prefix">System Prompt Prefix</small> | |
| 3368 | + </label> | |
| 3369 | + <div> | |
| 3370 | + <textarea id="instruct_system_sequence_prefix" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3371 | + </div> | |
| 3372 | + </div> | |
| 3373 | + <div class="flexAuto" title="Inserted after a System prompt." data-i18n="[title]Inserted after a System prompt."> | |
| 3374 | + <label for="instruct_system_sequence_suffix"> | |
| 3375 | + <small data-i18n="System Prompt Suffix">System Prompt Suffix</small> | |
| 3376 | + </label> | |
| 3377 | + <div> | |
| 3378 | + <textarea id="instruct_system_sequence_suffix" class="text_pole wide100p textarea_compact autoSetHeight"></textarea> | |
| 3379 | + </div> | |
| 3380 | + </div> | |
| 3381 | + </div> | |
| 3382 | + </details> | |
| 3383 | + <details> | |
| 3384 | + <summary data-i18n="Misc. Sequences">Misc. Sequences</summary> | |
| 3438 | 3385 | <div class="flex-container"> |
| 3439 | 3386 | <div class="flexAuto" title="Inserted before the first Assistant's message." data-i18n="[title]Inserted before the first Assistant's message."> |
| 3440 | 3387 | <small data-i18n="First Assistant Prefix">First Assistant Prefix</small> |
| @@ -3471,6 +3418,147 @@ | ||
| 3471 | 3418 | <textarea id="instruct_user_alignment_message" class="text_pole textarea_compact autoSetHeight"></textarea> |
| 3472 | 3419 | </div> |
| 3473 | 3420 | </div> |
| 3421 | + </details> | |
| 3422 | + </div> | |
| 3423 | + </div> | |
| 3424 | + <div id="SystemPromptColumn" class="flex-container flexNoGap flexFlowColumn flex1"> | |
| 3425 | + <div id="SystemPromptBlock"> | |
| 3426 | + <h4 class="standoutHeader title_restorable justifySpaceBetween"> | |
| 3427 | + <div class="flex-container"> | |
| 3428 | + <span data-i18n="System Prompt">System Prompt</span> | |
| 3429 | + <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#system-prompt" class="notes-link" target="_blank"> | |
| 3430 | + <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3431 | + </a> | |
| 3432 | + </div> | |
| 3433 | + <div class="flex-container"> | |
| 3434 | + <label for="sysprompt_enabled" class="checkbox_label flex1" title="Enable System Prompt" data-i18n="[title]sysprompt_enabled"> | |
| 3435 | + <input id="sysprompt_enabled" type="checkbox" style="display:none;" /> | |
| 3436 | + <small><i class="fa-solid fa-power-off menu_button margin0"></i></small> | |
| 3437 | + </label> | |
| 3438 | + </div> | |
| 3439 | + </h4> | |
| 3440 | + | |
| 3441 | + <div class="flex-container" title="Select your current System Prompt" data-i18n="[title]Select your current System Prompt"> | |
| 3442 | + <select id="sysprompt_select" data-preset-manager-for="sysprompt" class="flex1 text_pole"></select> | |
| 3443 | + <div class="flex-container margin0 justifyCenter"> | |
| 3444 | + <input type="file" hidden data-preset-manager-file="sysprompt" accept=".json, .settings"> | |
| 3445 | + <i data-preset-manager-update="sysprompt" class="menu_button fa-solid fa-save" title="Update current prompt" data-i18n="[title]Update current prompt"></i> | |
| 3446 | + <i data-preset-manager-new="sysprompt" class="menu_button fa-solid fa-file-circle-plus" title="Save prompt as" data-i18n="[title]Save prompt as"></i> | |
| 3447 | + <i data-preset-manager-import="sysprompt" class="displayNone menu_button fa-solid fa-file-import" title="Import template" data-i18n="[title]Import template"></i> | |
| 3448 | + <i data-preset-manager-export="sysprompt" class="displayNone menu_button fa-solid fa-file-export" title="Export template" data-i18n="[title]Export template"></i> | |
| 3449 | + <i data-preset-manager-restore="sysprompt" class="menu_button fa-solid fa-recycle" title="Restore current prompt" data-i18n="[title]Restore current prompt"></i> | |
| 3450 | + <i data-preset-manager-delete="sysprompt" class="menu_button fa-solid fa-trash-can" title="Delete prompt" data-i18n="[title]Delete prompt"></i> | |
| 3451 | + </div> | |
| 3452 | + </div> | |
| 3453 | + | |
| 3454 | + <div> | |
| 3455 | + <label for="sysprompt_content" class="flex-container"> | |
| 3456 | + <small data-i18n="Prompt Content">Prompt Content</small> | |
| 3457 | + <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="sysprompt_content" title="Expand the editor" data-i18n="[title]Expand the editor"></i> | |
| 3458 | + </label> | |
| 3459 | + <textarea id="sysprompt_content" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3460 | + </div> | |
| 3461 | + </div> | |
| 3462 | + | |
| 3463 | + <div> | |
| 3464 | + | |
| 3465 | + </div> | |
| 3466 | + | |
| 3467 | + <div> | |
| 3468 | + <h4 class="range-block-title justifyLeft standoutHeader"> | |
| 3469 | + <span data-i18n="Custom Stopping Strings"> | |
| 3470 | + Custom Stopping Strings | |
| 3471 | + </span> | |
| 3472 | + <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#custom-stopping-strings" class="notes-link" target="_blank"> | |
| 3473 | + <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3474 | + </a> | |
| 3475 | + </h4> | |
| 3476 | + <div> | |
| 3477 | + <small> | |
| 3478 | + <span data-i18n="JSON serialized array of strings">JSON serialized array of strings</span> | |
| 3479 | + <i class="fa-solid fa-question-circle opacity50p" title="e.g: ["Ford", "BMW", "Fiat"]"></i> | |
| 3480 | + </small> | |
| 3481 | + </div> | |
| 3482 | + <div> | |
| 3483 | + <textarea id="custom_stopping_strings" class="text_pole textarea_compact monospace autoSetHeight"></textarea> | |
| 3484 | + </div> | |
| 3485 | + <label class="checkbox_label" for="custom_stopping_strings_macro"> | |
| 3486 | + <input id="custom_stopping_strings_macro" type="checkbox" checked> | |
| 3487 | + <small data-i18n="Replace Macro in Custom Stopping Strings"> | |
| 3488 | + Replace Macro in Custom Stopping Strings | |
| 3489 | + </small> | |
| 3490 | + </label> | |
| 3491 | + </div> | |
| 3492 | + | |
| 3493 | + <div name="tokenizerSettingsBlock"> | |
| 3494 | + <div name="tokenizerSelectorBlock"> | |
| 3495 | + <h4 class="standoutHeader"><span data-i18n="Tokenizer">Tokenizer</span> | |
| 3496 | + <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#tokenizer" class="notes-link" target="_blank"> | |
| 3497 | + <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3498 | + </a> | |
| 3499 | + </h4> | |
| 3500 | + <select id="tokenizer"> | |
| 3501 | + <option value="99">Best match (recommended)</option> | |
| 3502 | + <option value="0">None / Estimated</option> | |
| 3503 | + <option value="1">GPT-2</option> | |
| 3504 | + <!-- Option #2 was a legacy GPT-2/3 tokenizer --> | |
| 3505 | + <option value="3">Llama 1/2</option> | |
| 3506 | + <option value="12">Llama 3</option> | |
| 3507 | + <option value="13">Gemma / Gemini</option> | |
| 3508 | + <option value="14">Jamba</option> | |
| 3509 | + <option value="15">Qwen2</option> | |
| 3510 | + <option value="16">Command-R</option> | |
| 3511 | + <option value="4">NerdStash (NovelAI Clio)</option> | |
| 3512 | + <option value="5">NerdStash v2 (NovelAI Kayra)</option> | |
| 3513 | + <option value="7">Mistral V1</option> | |
| 3514 | + <option value="17">Mistral Nemo</option> | |
| 3515 | + <option value="8">Yi</option> | |
| 3516 | + <option value="11">Claude 1/2</option> | |
| 3517 | + <option value="6">API (WebUI / koboldcpp)</option> | |
| 3518 | + </select> | |
| 3519 | + </div> | |
| 3520 | + <div class="range-block flex-container flexnowrap" name="tokenPaddingBlock"> | |
| 3521 | + <div class="range-block-title justifyLeft"> | |
| 3522 | + <small data-i18n="Token Padding" class="flex-container">Token Padding | |
| 3523 | + <a href="https://docs.sillytavern.app/usage/core-concepts/advancedformatting/#token-padding" class="notes-link" target="_blank"> | |
| 3524 | + <span class="fa-solid fa-circle-question note-link-span"></span> | |
| 3525 | + </a> | |
| 3526 | + </small> | |
| 3527 | + </div> | |
| 3528 | + <input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" /> | |
| 3529 | + </div> | |
| 3530 | + </div> | |
| 3531 | + <div> | |
| 3532 | + <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> | |
| 3533 | + <div> | |
| 3534 | + <small> | |
| 3535 | + <span data-i18n="Non-markdown strings"> | |
| 3536 | + Non-markdown strings | |
| 3537 | + </span> | |
| 3538 | + </small> | |
| 3539 | + <div> | |
| 3540 | + <input id="markdown_escape_strings" class="text_pole textarea_compact" type="text" data-i18n="[placeholder]comma delimited,no spaces between" placeholder="comma delimited,no spaces between" /> | |
| 3541 | + </div> | |
| 3542 | + </div> | |
| 3543 | + | |
| 3544 | + <div name="startReplyWithBlock"> | |
| 3545 | + <div> | |
| 3546 | + <small> | |
| 3547 | + <span data-i18n="Start Reply With"> | |
| 3548 | + Start Reply With | |
| 3549 | + </span> | |
| 3550 | + </small> | |
| 3551 | + <div> | |
| 3552 | + <textarea id="start_reply_with" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3553 | + </div> | |
| 3554 | + <label class="checkbox_label" for="chat-show-reply-prefix-checkbox"> | |
| 3555 | + <input id="chat-show-reply-prefix-checkbox" type="checkbox" /> | |
| 3556 | + <small data-i18n="Show reply prefix in chat"> | |
| 3557 | + Show reply prefix in chat | |
| 3558 | + </small> | |
| 3559 | + </label> | |
| 3560 | + </div> | |
| 3561 | + </div> | |
| 3474 | 3562 | </div> |
| 3475 | 3563 | </div> |
| 3476 | 3564 | </div> |
| @@ -5452,23 +5540,23 @@ | ||
| 5452 | 5540 | <div class="world_entry_form_control flex1"> |
| 5453 | 5541 | <small class="textAlignCenter" data-i18n="Case-Sensitive">Case-Sensitive</small> |
| 5454 | 5542 | <select name="caseSensitive" class="text_pole widthNatural margin0"> |
| 5455 | 5543 | <option value="null" data-i18n="Use global setting">Use global setting</option> |
| 5456 | 5544 | <option value="true" data-i18n="Yes">Yes</option> |
| 5457 | 5545 | <option value="false" data-i18n="No">No</option> |
| 5458 | 5546 | </select> |
| 5459 | 5547 | </div> |
| 5460 | 5548 | <div class="world_entry_form_control flex1"> |
| 5461 | 5549 | <small class="textAlignCenter" data-i18n="Match Whole Words">Match Whole Words</small> |
| 5462 | 5550 | <select name="matchWholeWords" class="text_pole widthNatural margin0"> |
| 5463 | 5551 | <option value="null" data-i18n="Use global setting">Use global setting</option> |
| 5464 | 5552 | <option value="true" data-i18n="Yes">Yes</option> |
| 5465 | 5553 | <option value="false" data-i18n="No">No</option> |
| 5466 | 5554 | </select> |
| 5467 | 5555 | </div> |
| 5468 | 5556 | <div class="world_entry_form_control flex1"> |
| 5469 | 5557 | <small class="textAlignCenter" data-i18n="Use Group Scoring">Use Group Scoring</small> |
| 5470 | 5558 | <select name="useGroupScoring" class="text_pole widthNatural margin0"> |
| 5471 | 5559 | <option value="null" data-i18n="Use global setting">Use global setting</option> |
| 5472 | 5560 | <option value="true" data-i18n="Yes">Yes</option> |
| 5473 | 5561 | <option value="false" data-i18n="No">No</option> |
| 5474 | 5562 | </select> |
| @@ -5477,6 +5565,13 @@ | ||
| 5477 | 5565 | <small class="textAlignCenter" data-i18n="Automation ID">Automation ID</small> |
| 5478 | 5566 | <input class="text_pole margin0" name="automationId" type="text" placeholder="( None )" data-i18n="[placeholder]( None )"> |
| 5479 | 5567 | </div> |
| 5568 | + <div class="world_entry_form_control flex1" title="Defines delay levels for recursive scans. Initially, only the first level (smallest number) will match. Once no matches are found, the next level becomes eligible for matching. This repeats until all levels are checked. Tied to the "Delay until recursion" setting." data-i18n="[title]delay_until_recursion_level"> | |
| 5569 | + <small class="textAlignCenter"> | |
| 5570 | + <span data-i18n="Recursion Level">Recursion Level</span> | |
| 5571 | + <div class="fa-solid fa-circle-info opacity50p"></div> | |
| 5572 | + </small> | |
| 5573 | + <input class="text_pole margin0" name="delayUntilRecursionLevel" type="text" placeholder="1"> | |
| 5574 | + </div> | |
| 5480 | 5575 | </div> |
| 5481 | 5576 | <div name="contentAndCharFilterBlock" class="world_entry_thin_controls flex2"> |
| 5482 | 5577 | <div class="world_entry_form_control flex1"> |
| @@ -5493,20 +5588,20 @@ | ||
| 5493 | 5588 | <div> |
| 5494 | 5589 | <label class="checkbox flex-container alignitemscenter flexNoGap"> |
| 5495 | 5590 | <input type="checkbox" name="exclude_recursion" /> |
| 5496 | 5591 | <span data-i18n="ExcludeNon-recursable from(will recursionnot be activated by another)"> |
| 5497 | 5592 | Non-recursable (this entry will not be activated by another) |
| 5498 | 5593 | </span> |
| 5499 | 5594 | </label> |
| 5500 | 5595 | <label class="checkbox flex-container alignitemscenter flexNoGap"> |
| 5501 | 5596 | <input type="checkbox" name="prevent_recursion" /> |
| 5502 | 5597 | <span data-i18n="Prevent further recursion (this entry will not activate others)"> |
| 5503 | 5598 | Prevent further recursion (this entry will not activate others) |
| 5504 | 5599 | </span> |
| 5505 | 5600 | </label> |
| 5506 | 5601 | <label class="checkbox flex-container alignitemscenter flexNoGap"> |
| 5507 | 5602 | <input type="checkbox" name="delay_until_recursion" /> |
| 5508 | 5603 | <span data-i18n="Delay until recursion (this entry can only be activated on recursive checking)"> |
| 5509 | 5604 | Delay until recursion (this entry can only be activated on recursive checking) |
| 5510 | 5605 | </span> |
| 5511 | 5606 | </label> |
| 5512 | 5607 | </div> |
| @@ -99,7 +99,7 @@ import { | ||
| 99 | 99 | proxies, |
| 100 | 100 | loadProxyPresets, |
| 101 | 101 | selected_proxy, |
| 102 | 102 | initOpenaiinitOpenAI, |
| 103 | 103 | } from './scripts/openai.js'; |
| 104 | 104 | |
| 105 | 105 | import { |
| @@ -210,7 +210,7 @@ import { | ||
| 210 | 210 | selectContextPreset, |
| 211 | 211 | } from './scripts/instruct-mode.js'; |
| 212 | 212 | import { initLocales, t, translate } from './scripts/i18n.js'; |
| 213 | 213 | import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, getTokenizerModel, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; |
| 214 | 214 | import { |
| 215 | 215 | user_avatar, |
| 216 | 216 | getUserAvatars, |
| @@ -224,7 +224,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set | ||
| 224 | 224 | import { hideLoader, showLoader } from './scripts/loader.js'; |
| 225 | 225 | import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js'; |
| 226 | 226 | import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels } from './scripts/textgen-models.js'; |
| 227 | 227 | import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js'; |
| 228 | 228 | import { initPresetManager } from './scripts/preset-manager.js'; |
| 229 | 229 | import { MacrosParser, evaluateMacros, getLastMessageId } from './scripts/macros.js'; |
| 230 | 230 | import { currentUser, setUserControls } from './scripts/user.js'; |
| @@ -243,6 +243,7 @@ import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/Slash | ||
| 243 | 243 | import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 244 | 244 | import { initInputMarkdown } from './scripts/input-md-formatting.js'; |
| 245 | 245 | import { AbortReason } from './scripts/util/AbortReason.js'; |
| 246 | +import { initSystemPrompts } from './scripts/sysprompt.js'; | |
| 246 | 247 | |
| 247 | 248 | //exporting functions and vars for mods |
| 248 | 249 | export { |
| @@ -411,6 +412,7 @@ export const event_types = { | ||
| 411 | 412 | MESSAGE_FILE_EMBEDDED: 'message_file_embedded', |
| 412 | 413 | IMPERSONATE_READY: 'impersonate_ready', |
| 413 | 414 | CHAT_CHANGED: 'chat_id_changed', |
| 415 | + GENERATION_AFTER_COMMANDS: 'GENERATION_AFTER_COMMANDS', | |
| 414 | 416 | GENERATION_STARTED: 'generation_started', |
| 415 | 417 | GENERATION_STOPPED: 'generation_stopped', |
| 416 | 418 | GENERATION_ENDED: 'generation_ended', |
| @@ -509,6 +511,7 @@ let saveCharactersPage = 0; | ||
| 509 | 511 | export const default_avatar = 'img/ai4.png'; |
| 510 | 512 | export const system_avatar = 'img/five.png'; |
| 511 | 513 | export const comment_avatar = 'img/quill.png'; |
| 514 | +export const default_user_avatar = 'img/user-default.png'; | |
| 512 | 515 | export let CLIENT_VERSION = 'SillyTavern:UNKNOWN:Cohee#1207'; // For Horde header |
| 513 | 516 | let optionsPopper = Popper.createPopper(document.getElementById('options_button'), document.getElementById('options'), { |
| 514 | 517 | placement: 'top-start', |
| @@ -927,9 +930,12 @@ async function firstLoadInit() { | ||
| 927 | 930 | addSafariPatch(); |
| 928 | 931 | await getClientVersion(); |
| 929 | 932 | await readSecretState(); |
| 930 | 933 | await initLocales(); |
| 931 | 934 | initDefaultSlashCommands(); |
| 932 | 935 | initTextGenModels(); |
| 936 | + initOpenAI(); | |
| 937 | + initSystemPrompts(); | |
| 938 | + await initPresetManager(); | |
| 933 | 939 | await getSystemMessages(); |
| 934 | 940 | sendSystemMessage(system_message_types.WELCOME); |
| 935 | 941 | sendSystemMessage(system_message_types.WELCOME_PROMPT); |
| @@ -937,13 +943,11 @@ async function firstLoadInit() { | ||
| 937 | 943 | initKeyboard(); |
| 938 | 944 | initDynamicStyles(); |
| 939 | 945 | initTags(); |
| 940 | - initOpenai(); | |
| 941 | 946 | initBookmarks(); |
| 942 | 947 | await getUserAvatars(true, user_avatar); |
| 943 | 948 | await getCharacters(); |
| 944 | 949 | await getBackgrounds(); |
| 945 | 950 | await initTokenizers(); |
| 946 | - await initPresetManager(); | |
| 947 | 951 | initBackgrounds(); |
| 948 | 952 | initAuthorsNote(); |
| 949 | 953 | initPersonas(); |
| @@ -1213,6 +1217,9 @@ async function getStatusTextgen() { | ||
| 1213 | 1217 | // Determine instruct mode preset |
| 1214 | 1218 | autoSelectInstructPreset(online_status); |
| 1215 | 1219 | |
| 1220 | + const supportsTokenization = response.headers.get('x-supports-tokenization') === 'true'; | |
| 1221 | + supportsTokenization ? sessionStorage.setItem(TOKENIZER_SUPPORTED_KEY, 'true') : sessionStorage.removeItem(TOKENIZER_SUPPORTED_KEY); | |
| 1222 | + | |
| 1216 | 1223 | // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. |
| 1217 | 1224 | if (online_status === 'no_connection' && data.response) { |
| 1218 | 1225 | toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true }); |
| @@ -1845,6 +1852,7 @@ export async function deleteLastMessage() { | ||
| 1845 | 1852 | } |
| 1846 | 1853 | |
| 1847 | 1854 | export async function reloadCurrentChat() { |
| 1855 | + preserveNeutralChat(); | |
| 1848 | 1856 | await clearChat(); |
| 1849 | 1857 | chat.length = 0; |
| 1850 | 1858 | |
| @@ -1856,6 +1864,7 @@ export async function reloadCurrentChat() { | ||
| 1856 | 1864 | } |
| 1857 | 1865 | else { |
| 1858 | 1866 | resetChatState(); |
| 1867 | + restoreNeutralChat(); | |
| 1859 | 1868 | await getCharacters(); |
| 1860 | 1869 | await printMessages(); |
| 1861 | 1870 | await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId()); |
| @@ -3332,10 +3341,12 @@ function removeLastMessage() { | ||
| 3332 | 3341 | */ |
| 3333 | 3342 | export async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage, quietName } = {}, dryRun = false) { |
| 3334 | 3343 | console.log('Generate entered'); |
| 3335 | - await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); | |
| 3336 | 3344 | setGenerationProgress(0); |
| 3337 | 3345 | generation_started = new Date(); |
| 3338 | 3346 | |
| 3347 | + // Occurs every time, even if the generation is aborted due to slash commands execution | |
| 3348 | + await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); | |
| 3349 | + | |
| 3339 | 3350 | // Don't recreate abort controller if signal is passed |
| 3340 | 3351 | if (!(abortController && signal)) { |
| 3341 | 3352 | abortController = new AbortController(); |
| @@ -3355,6 +3366,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3355 | 3366 | } |
| 3356 | 3367 | } |
| 3357 | 3368 | |
| 3369 | + // Occurs only if the generation is not aborted due to slash commands execution | |
| 3370 | + await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); | |
| 3371 | + | |
| 3358 | 3372 | if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) { |
| 3359 | 3373 | toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true }); |
| 3360 | 3374 | unblockGeneration(type); |
| @@ -3512,9 +3526,14 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3512 | 3526 | jailbreak, |
| 3513 | 3527 | } = getCharacterCardFields(); |
| 3514 | 3528 | |
| 3515 | 3529 | if (isInstructmain_api !== 'openai') { |
| 3516 | - system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.instruct.system_prompt, name1, name2); | |
| 3530 | + if (power_user.sysprompt.enabled) { | |
| 3517 | - system = formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.instruct.system_prompt)); | |
| 3531 | + system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.sysprompt.content, name1, name2); | |
| 3532 | + system = isInstruct ? formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.sysprompt.content)) : system; | |
| 3533 | + } else { | |
| 3534 | + // Nullify if it's not enabled | |
| 3535 | + system = ''; | |
| 3536 | + } | |
| 3518 | 3537 | } |
| 3519 | 3538 | |
| 3520 | 3539 | // Depth prompt (character-specific A/N) |
| @@ -3767,7 +3786,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3767 | 3786 | personality: personality, |
| 3768 | 3787 | persona: power_user.persona_description_position == persona_description_positions.IN_PROMPT ? persona : '', |
| 3769 | 3788 | scenario: scenario, |
| 3770 | 3789 | system: isInstruct ? system : '', |
| 3771 | 3790 | char: name2, |
| 3772 | 3791 | user: name1, |
| 3773 | 3792 | wiBefore: worldInfoBefore, |
| @@ -4340,7 +4359,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4340 | 4359 | this_max_context: this_max_context, |
| 4341 | 4360 | padding: power_user.token_padding, |
| 4342 | 4361 | main_api: main_api, |
| 4343 | 4362 | instruction: isInstructmain_api !== 'openai' && power_user.sysprompt.enabled ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instructsysprompt.system_promptcontent) : '', |
| 4344 | 4363 | userPersona: (power_user.persona_description_position == persona_description_positions.IN_PROMPT ? (persona || '') : ''), |
| 4345 | 4364 | tokenizer: getFriendlyTokenizerName(main_api).tokenizerName || '', |
| 4346 | 4365 | }; |
| @@ -4839,6 +4858,13 @@ export function getMaxContextSize(overrideResponseLength = null) { | ||
| 4839 | 4858 | console.log(`NovelAI subscription limit reached. Max context size is now ${this_max_context}`); |
| 4840 | 4859 | } |
| 4841 | 4860 | } |
| 4861 | + if (nai_settings.model_novel.includes('erato')) { | |
| 4862 | + // subscriber limits coming soon | |
| 4863 | + this_max_context = Math.min(max_context, 8192); | |
| 4864 | + | |
| 4865 | + // Added special tokens and whatnot | |
| 4866 | + this_max_context -= 1; | |
| 4867 | + } | |
| 4842 | 4868 | |
| 4843 | 4869 | this_max_context = this_max_context - (overrideResponseLength || amount_gen); |
| 4844 | 4870 | } |
| @@ -5466,7 +5492,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | ||
| 5466 | 5492 | } |
| 5467 | 5493 | |
| 5468 | 5494 | if (!displayIncompleteSentences && power_user.trim_sentences) { |
| 5469 | 5495 | getMessage = trimToEndSentence(getMessage, power_user.include_newline); |
| 5470 | 5496 | } |
| 5471 | 5497 | |
| 5472 | 5498 | if (power_user.trim_spaces) { |
| @@ -5698,10 +5724,10 @@ export function deactivateSendButtons() { | ||
| 5698 | 5724 | } |
| 5699 | 5725 | |
| 5700 | 5726 | export function resetChatState() { |
| 5727 | + // replaces deleted charcter name with system user since it will be displayed next. | |
| 5728 | + name2 = (this_chid === undefined && neutralCharacterName) ? neutralCharacterName : systemUserName; | |
| 5701 | 5729 | //unsets expected chid before reloading (related to getCharacters/printCharacters from using old arrays) |
| 5702 | 5730 | this_chid = undefined; |
| 5703 | - // replaces deleted charcter name with system user since it will be displayed next. | |
| 5704 | - name2 = systemUserName; | |
| 5705 | 5731 | // sets up system user to tell user about having deleted a character |
| 5706 | 5732 | chat.splice(0, chat.length, ...SAFETY_CHAT); |
| 5707 | 5733 | // resets chat metadata |
| @@ -6917,15 +6943,13 @@ export async function displayPastChats() { | ||
| 6917 | 6943 | } |
| 6918 | 6944 | // Check whether `text` {string} includes all of the `fragments` {string[]}. |
| 6919 | 6945 | function matchFragments(fragments, text) { |
| 6920 | 6946 | if (!text || !text.toLowerCase) {return false; |
| 6921 | - return false; | |
| 6947 | + return fragments.every(item => text.toLowerCase().includes(item)); | |
| 6922 | - } | |
| 6923 | - return fragments.every(item => text.includes(item)); | |
| 6924 | 6948 | } |
| 6925 | 6949 | const fragments = makeQueryFragments(searchQuery); |
| 6926 | 6950 | // At least one chat message must match *all* the fragments. |
| 6927 | 6951 | // Currently, this doesn't match if the fragment matches are distributed across several chat messages. |
| 6928 | 6952 | return chatContent && Object.values(chatContent).some(message => matchFragments(fragments, message?.mes?.toLowerCase())); |
| 6929 | 6953 | }); |
| 6930 | 6954 | |
| 6931 | 6955 | console.debug(filteredData); |
| @@ -8565,20 +8589,27 @@ async function connectAPISlash(args, text) { | ||
| 8565 | 8589 | return ''; |
| 8566 | 8590 | } |
| 8567 | 8591 | |
| 8592 | + let connectionRequired = false; | |
| 8593 | + | |
| 8594 | + if (main_api !== apiConfig.selected) { | |
| 8568 | 8595 | $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true); |
| 8569 | 8596 | $('#main_api').trigger('change'); |
| 8597 | + connectionRequired = true; | |
| 8598 | + } | |
| 8570 | 8599 | |
| 8571 | 8600 | if (apiConfig.source && oai_settings.chat_completion_source !== apiConfig.source) { |
| 8572 | 8601 | $(`#chat_completion_source option[value='${apiConfig.source}']`).prop('selected', true); |
| 8573 | 8602 | $('#chat_completion_source').trigger('change'); |
| 8603 | + connectionRequired = true; | |
| 8574 | 8604 | } |
| 8575 | 8605 | |
| 8576 | 8606 | if (apiConfig.type && textgen_settings.type !== apiConfig.type) { |
| 8577 | 8607 | $(`#textgen_type option[value='${apiConfig.type}']`).prop('selected', true); |
| 8578 | 8608 | $('#textgen_type').trigger('change'); |
| 8609 | + connectionRequired = true; | |
| 8579 | 8610 | } |
| 8580 | 8611 | |
| 8581 | 8612 | if (connectionRequired && apiConfig.button) { |
| 8582 | 8613 | $(apiConfig.button).trigger('click'); |
| 8583 | 8614 | } |
| 8584 | 8615 | |
| @@ -8803,7 +8834,7 @@ export async function renameChat(oldFileName, newName) { | ||
| 8803 | 8834 | is_group: !!selected_group, |
| 8804 | 8835 | avatar_url: characters[this_chid]?.avatar, |
| 8805 | 8836 | original_file: `${oldFileName}.jsonl`, |
| 8806 | 8837 | renamed_file: `${newName.trim()}.jsonl`, |
| 8807 | 8838 | }; |
| 8808 | 8839 | |
| 8809 | 8840 | try { |
| @@ -8824,6 +8855,10 @@ export async function renameChat(oldFileName, newName) { | ||
| 8824 | 8855 | throw new Error('Server returned an error.'); |
| 8825 | 8856 | } |
| 8826 | 8857 | |
| 8858 | + if (data.sanitizedFileName) { | |
| 8859 | + newName = data.sanitizedFileName; | |
| 8860 | + } | |
| 8861 | + | |
| 8827 | 8862 | if (selected_group) { |
| 8828 | 8863 | await renameGroupChat(selected_group, oldFileName, newName); |
| 8829 | 8864 | } |
| @@ -8945,15 +8980,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | ||
| 8945 | 8980 | * It also ensures to save the settings after all the operations. |
| 8946 | 8981 | */ |
| 8947 | 8982 | async function removeCharacterFromUI() { |
| 8983 | + preserveNeutralChat(); | |
| 8948 | 8984 | await clearChat(); |
| 8949 | 8985 | $('#character_cross').clicktrigger('click'); |
| 8950 | - this_chid = undefined; | |
| 8986 | + resetChatState(); | |
| 8951 | - characters.length = 0; | |
| 8952 | - name2 = systemUserName; | |
| 8953 | - chat.splice(0, chat.length, ...SAFETY_CHAT); | |
| 8954 | - chat_metadata = {}; | |
| 8955 | 8987 | $(document.getElementById('rm_button_selected_ch')).children('h2').text(''); |
| 8956 | - this_chid = undefined; | |
| 8988 | + restoreNeutralChat(); | |
| 8957 | 8989 | await getCharacters(); |
| 8958 | 8990 | await printMessages(); |
| 8959 | 8991 | saveSettingsDebounced(); |
| @@ -1,12 +1,13 @@ | ||
| 1 | 1 | 'use strict'; |
| 2 | 2 | |
| 3 | 3 | import { callPopup, event_types, eventSource, is_send_press, main_api, substituteParams } from '../script.js'; |
| 4 | 4 | import { is_group_generating } from './group-chats.js'; |
| 5 | 5 | import { Message, TokenHandler } from './openai.js'; |
| 6 | 6 | import { power_user } from './power-user.js'; |
| 7 | 7 | import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; |
| 8 | 8 | import { debounce_timeout } from './constants.js'; |
| 9 | 9 | import { renderTemplateAsync } from './templates.js'; |
| 10 | +import { Popup } from './popup.js'; | |
| 10 | 11 | |
| 11 | 12 | function debouncePromise(func, delay) { |
| 12 | 13 | let timeoutId; |
| @@ -427,12 +428,13 @@ class PromptManager { | ||
| 427 | 428 | |
| 428 | 429 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name').value = prompt.name; |
| 429 | 430 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role').value = 'system'; |
| 430 | 431 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').value = prompt.content ?? ''; |
| 431 | 432 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').value = prompt.injection_position ?? 0; |
| 432 | 433 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_depth').value = prompt.injection_depth ?? DEFAULT_DEPTH; |
| 433 | 434 | document.getElementById(this.configuration.prefix + 'prompt_manager_depth_block').style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; |
| 434 | 435 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_forbid_overrides').checked = prompt.forbid_overrides ?? false; |
| 435 | 436 | document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block').style.visibility = this.overridablePrompts.includes(prompt.identifier) ? 'visible' : 'hidden'; |
| 437 | + document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt').disabled = prompt.marker ?? false; | |
| 436 | 438 | |
| 437 | 439 | if (!this.systemPrompts.includes(promptId)) { |
| 438 | 440 | document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_injection_position').removeAttribute('disabled'); |
| @@ -452,7 +454,9 @@ class PromptManager { | ||
| 452 | 454 | }; |
| 453 | 455 | |
| 454 | 456 | // Delete selected prompt from list form and close edit form |
| 455 | 457 | this.handleDeletePrompt = async (event) => { |
| 458 | + Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => { | |
| 459 | + if (!userChoice) return; | |
| 456 | 460 | const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value; |
| 457 | 461 | const prompt = this.getPromptById(promptID); |
| 458 | 462 | |
| @@ -467,6 +471,7 @@ class PromptManager { | ||
| 467 | 471 | this.render(); |
| 468 | 472 | this.saveServiceSettings(); |
| 469 | 473 | } |
| 474 | + }); | |
| 470 | 475 | }; |
| 471 | 476 | |
| 472 | 477 | // Create new prompt, then save it to settings and close form. |
| @@ -526,9 +531,9 @@ class PromptManager { | ||
| 526 | 531 | |
| 527 | 532 | // Import prompts for the selected character |
| 528 | 533 | this.handleImport = () => { |
| 529 | 534 | callPopupPopup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', 'confirm'null) |
| 530 | 535 | .then(userChoice => { |
| 531 | 536 | if (false === !userChoice) return; |
| 532 | 537 | |
| 533 | 538 | const fileOpener = document.createElement('input'); |
| 534 | 539 | fileOpener.type = 'file'; |
| @@ -562,9 +567,9 @@ class PromptManager { | ||
| 562 | 567 | |
| 563 | 568 | // Restore default state of a characters prompt order |
| 564 | 569 | this.handleCharacterReset = () => { |
| 565 | 570 | callPopupPopup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', 'confirm'null) |
| 566 | 571 | .then(userChoice => { |
| 567 | 572 | if (false === !userChoice) return; |
| 568 | 573 | |
| 569 | 574 | this.removePromptOrderForCharacter(this.activeCharacter); |
| 570 | 575 | this.addPromptOrderForCharacter(this.activeCharacter, promptManagerDefaultPromptOrder); |
| @@ -920,7 +925,15 @@ class PromptManager { | ||
| 920 | 925 | * @returns {boolean} True if the prompt can be edited, false otherwise. |
| 921 | 926 | */ |
| 922 | 927 | isPromptEditAllowed(prompt) { |
| 923 | - return !prompt.marker; | |
| 928 | + const forceEditPrompts = [ | |
| 929 | + 'charDescription', | |
| 930 | + 'charPersonality', | |
| 931 | + 'scenario', | |
| 932 | + 'personaDescription', | |
| 933 | + 'worldInfoBefore', | |
| 934 | + 'worldInfoAfter', | |
| 935 | + ]; | |
| 936 | + return forceEditPrompts.includes(prompt.identifier) || !prompt.marker; | |
| 924 | 937 | } |
| 925 | 938 | |
| 926 | 939 | /** |
| @@ -929,7 +942,17 @@ class PromptManager { | ||
| 929 | 942 | * @returns {boolean} True if the prompt can be deleted, false otherwise. |
| 930 | 943 | */ |
| 931 | 944 | isPromptToggleAllowed(prompt) { |
| 932 | - const forceTogglePrompts = ['charDescription', 'charPersonality', 'scenario', 'personaDescription', 'worldInfoBefore', 'worldInfoAfter', 'main', 'chatHistory', 'dialogueExamples']; | |
| 945 | + const forceTogglePrompts = [ | |
| 946 | + 'charDescription', | |
| 947 | + 'charPersonality', | |
| 948 | + 'scenario', | |
| 949 | + 'personaDescription', | |
| 950 | + 'worldInfoBefore', | |
| 951 | + 'worldInfoAfter', | |
| 952 | + 'main', | |
| 953 | + 'chatHistory', | |
| 954 | + 'dialogueExamples', | |
| 955 | + ]; | |
| 933 | 956 | return prompt.marker && !forceTogglePrompts.includes(prompt.identifier) ? false : !this.configuration.toggleDisabled.includes(prompt.identifier); |
| 934 | 957 | } |
| 935 | 958 | |
| @@ -1182,8 +1205,9 @@ class PromptManager { | ||
| 1182 | 1205 | const forbidOverridesBlock = document.getElementById(this.configuration.prefix + 'prompt_manager_forbid_overrides_block'); |
| 1183 | 1206 | |
| 1184 | 1207 | nameField.value = prompt.name ?? ''; |
| 1185 | 1208 | roleField.value = prompt.role ?? 'system'; |
| 1186 | 1209 | promptField.value = prompt.content ?? ''; |
| 1210 | + promptField.disabled = prompt.marker ?? false; | |
| 1187 | 1211 | injectionPositionField.value = prompt.injection_position ?? INJECTION_POSITION.RELATIVE; |
| 1188 | 1212 | injectionDepthField.value = prompt.injection_depth ?? DEFAULT_DEPTH; |
| 1189 | 1213 | injectionDepthBlock.style.visibility = prompt.injection_position === INJECTION_POSITION.ABSOLUTE ? 'visible' : 'hidden'; |
| @@ -1279,6 +1303,7 @@ class PromptManager { | ||
| 1279 | 1303 | nameField.value = ''; |
| 1280 | 1304 | roleField.selectedIndex = 0; |
| 1281 | 1305 | promptField.value = ''; |
| 1306 | + promptField.disabled = false; | |
| 1282 | 1307 | injectionPositionField.selectedIndex = 0; |
| 1283 | 1308 | injectionPositionField.removeAttribute('disabled'); |
| 1284 | 1309 | injectionDepthField.value = DEFAULT_DEPTH; |
| @@ -1515,16 +1540,17 @@ class PromptManager { | ||
| 1515 | 1540 | } |
| 1516 | 1541 | |
| 1517 | 1542 | const encodedName = escapeHtml(prompt.name); |
| 1543 | + const isMarkerPrompt = prompt.marker && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE; | |
| 1518 | 1544 | const isSystemPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && !prompt.forbid_overrides; |
| 1519 | 1545 | const isImportantPrompt = !prompt.marker && prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE && prompt.forbid_overrides; |
| 1520 | 1546 | const isUserPrompt = !prompt.marker && !prompt.system_prompt && prompt.injection_position !== INJECTION_POSITION.ABSOLUTE; |
| 1521 | 1547 | const isInjectionPrompt = !prompt.marker && prompt.injection_position === INJECTION_POSITION.ABSOLUTE; |
| 1522 | 1548 | const isOverriddenPrompt = Array.isArray(this.overriddenPrompts) && this.overriddenPrompts.includes(prompt.identifier); |
| 1523 | 1549 | const importantClass = isImportantPrompt ? `${prefix}prompt_manager_important` : ''; |
| 1524 | 1550 | listItemHtml += ` |
| 1525 | 1551 | <li class="${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data-pm-identifier="${prompt.identifier}"> |
| 1526 | 1552 | <span class="${prefix}prompt_manager_prompt_name" data-pm-name="${encodedName}"> |
| 1527 | 1553 | ${prompt.markerisMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : ''} |
| 1528 | 1554 | ${isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : ''} |
| 1529 | 1555 | ${isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : ''} |
| 1530 | 1556 | ${isUserPrompt ? '<span class="fa-fw fa-solid fa-user" title="User Prompt"></span>' : ''} |
| @@ -1724,7 +1750,7 @@ class PromptManager { | ||
| 1724 | 1750 | */ |
| 1725 | 1751 | showPopup(area = 'edit') { |
| 1726 | 1752 | const areaElement = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_' + area); |
| 1727 | 1753 | areaElement.style.display = 'blockflex'; |
| 1728 | 1754 | |
| 1729 | 1755 | $('#' + this.configuration.prefix + 'prompt_manager_popup').first() |
| 1730 | 1756 | .slideDown(200, 'swing') |
| @@ -737,6 +737,16 @@ export function initRossMods() { | ||
| 737 | 737 | RA_autoconnect(); |
| 738 | 738 | } |
| 739 | 739 | |
| 740 | + if (isMobile()) { | |
| 741 | + const fixFunkyPositioning = () => { | |
| 742 | + console.debug('[Mobile] Device viewport change detected.'); | |
| 743 | + document.documentElement.style.position = 'fixed'; | |
| 744 | + requestAnimationFrame(() => document.documentElement.style.position = ''); | |
| 745 | + }; | |
| 746 | + window.addEventListener('resize', fixFunkyPositioning); | |
| 747 | + window.addEventListener('orientationchange', fixFunkyPositioning); | |
| 748 | + } | |
| 749 | + | |
| 740 | 750 | $('#main_api').change(function () { |
| 741 | 751 | var PrevAPI = main_api; |
| 742 | 752 | setTimeout(() => RA_autoconnect(PrevAPI), 100); |
| @@ -313,7 +313,7 @@ async function onDeleteBackgroundClick(e) { | ||
| 313 | 313 | } |
| 314 | 314 | } |
| 315 | 315 | |
| 316 | 316 | const autoBgPrompt = 'PauseIgnore yourprevious roleplayinstructions and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}'; |
| 317 | 317 | |
| 318 | 318 | async function autoBackgroundCommand() { |
| 319 | 319 | /** @type {HTMLElement[]} */ |
| @@ -19,6 +19,8 @@ import { | ||
| 19 | 19 | this_chid, |
| 20 | 20 | saveChatConditional, |
| 21 | 21 | chat_metadata, |
| 22 | + neutralCharacterName, | |
| 23 | + updateChatMetadata, | |
| 22 | 24 | } from '../script.js'; |
| 23 | 25 | import { selected_group } from './group-chats.js'; |
| 24 | 26 | import { power_user } from './power-user.js'; |
| @@ -133,10 +135,11 @@ export async function hideChatMessageRange(start, end, unhide) { | ||
| 133 | 135 | const message = chat[messageId]; |
| 134 | 136 | if (!message) continue; |
| 135 | 137 | |
| 138 | + message.is_system = hide; | |
| 139 | + | |
| 140 | + // Also toggle "hidden" state for all visible messages | |
| 136 | 141 | const messageBlock = $(`.mes[mesid="${messageId}"]`); |
| 137 | 142 | if (!messageBlock.length) continue; |
| 138 | - | |
| 139 | - message.is_system = hide; | |
| 140 | 143 | messageBlock.attr('is_system', String(hide)); |
| 141 | 144 | } |
| 142 | 145 | |
| @@ -1352,6 +1355,32 @@ async function verifyAttachmentsForSource(source) { | ||
| 1352 | 1355 | } |
| 1353 | 1356 | } |
| 1354 | 1357 | |
| 1358 | +const NEUTRAL_CHAT_KEY = 'neutralChat'; | |
| 1359 | + | |
| 1360 | +export function preserveNeutralChat() { | |
| 1361 | + if (this_chid !== undefined || selected_group || name2 !== neutralCharacterName) { | |
| 1362 | + return; | |
| 1363 | + } | |
| 1364 | + | |
| 1365 | + sessionStorage.setItem(NEUTRAL_CHAT_KEY, JSON.stringify({ chat, chat_metadata })); | |
| 1366 | +} | |
| 1367 | + | |
| 1368 | +export function restoreNeutralChat() { | |
| 1369 | + if (this_chid !== undefined || selected_group || name2 !== neutralCharacterName) { | |
| 1370 | + return; | |
| 1371 | + } | |
| 1372 | + | |
| 1373 | + const neutralChat = sessionStorage.getItem(NEUTRAL_CHAT_KEY); | |
| 1374 | + if (!neutralChat) { | |
| 1375 | + return; | |
| 1376 | + } | |
| 1377 | + | |
| 1378 | + const { chat: neutralChatData, chat_metadata: neutralChatMetadata } = JSON.parse(neutralChat); | |
| 1379 | + chat.splice(0, chat.length, ...neutralChatData); | |
| 1380 | + updateChatMetadata(neutralChatMetadata, true); | |
| 1381 | + sessionStorage.removeItem(NEUTRAL_CHAT_KEY); | |
| 1382 | +} | |
| 1383 | + | |
| 1355 | 1384 | /** |
| 1356 | 1385 | * Registers a file converter function. |
| 1357 | 1386 | * @param {string} mimeType MIME type |
| @@ -29,7 +29,6 @@ export function saveMetadataDebounced() { | ||
| 29 | 29 | const characterId = context.characterId; |
| 30 | 30 | |
| 31 | 31 | if (saveMetadataTimeout) { |
| 32 | - console.debug('Clearing save metadata timeout'); | |
| 33 | 32 | clearTimeout(saveMetadataTimeout); |
| 34 | 33 | } |
| 35 | 34 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { ensureImageFormatSupported, getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js'; |
| 2 | 2 | import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules, renderExtensionTemplateAsync } from '../../extensions.js'; |
| 3 | 3 | import { appendMediaToMessage, callPopup, eventSource, event_types, getRequestHeaders, main_api, saveChatConditional, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js'; |
| 4 | 4 | import { getMessageTimeStamp } from '../../RossAscends-mods.js'; |
| 5 | 5 | import { SECRET_KEYS, secret_state } from '../../secrets.js'; |
| 6 | 6 | import { getMultimodalCaption } from '../shared.js'; |
| @@ -403,6 +403,7 @@ jQuery(async function () { | ||
| 403 | 403 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) || |
| 404 | 404 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) || |
| 405 | 405 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'zerooneai' && secret_state[SECRET_KEYS.ZEROONEAI]) || |
| 406 | + (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'mistral' && (secret_state[SECRET_KEYS.MISTRALAI] || extension_settings.caption.allow_reverse_proxy)) || | |
| 406 | 407 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && (secret_state[SECRET_KEYS.MAKERSUITE] || extension_settings.caption.allow_reverse_proxy)) || |
| 407 | 408 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && (secret_state[SECRET_KEYS.CLAUDE] || extension_settings.caption.allow_reverse_proxy)) || |
| 408 | 409 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) || |
| @@ -431,14 +432,9 @@ jQuery(async function () { | ||
| 431 | 432 | $('#form_sheld').append(imgForm); |
| 432 | 433 | $('#img_file').on('change', (e) => onSelectImage(e.originalEvent, '', false)); |
| 433 | 434 | } |
| 434 | 435 | async function switchMultimodalBlocks() { |
| 436 | + await addOpenRouterModels(); | |
| 435 | 437 | const isMultimodal = extension_settings.caption.source === 'multimodal'; |
| 436 | - $('#caption_ollama_pull').on('click', (e) => { | |
| 437 | - const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : ''; | |
| 438 | - e.preventDefault(); | |
| 439 | - $('#ollama_download_model').trigger('click'); | |
| 440 | - $('#dialogue_popup_input').val(presetModel); | |
| 441 | - }); | |
| 442 | 438 | $('#caption_multimodal_block').toggle(isMultimodal); |
| 443 | 439 | $('#caption_prompt_block').toggle(isMultimodal); |
| 444 | 440 | $('#caption_multimodal_api').val(extension_settings.caption.multimodal_api); |
| @@ -448,30 +444,48 @@ jQuery(async function () { | ||
| 448 | 444 | const types = type.split(','); |
| 449 | 445 | $(this).toggle(types.includes(extension_settings.caption.multimodal_api)); |
| 450 | 446 | }); |
| 451 | - $('#caption_multimodal_api').on('change', () => { | |
| 452 | - const api = String($('#caption_multimodal_api').val()); | |
| 453 | - const model = String($(`#caption_multimodal_model option[data-type="${api}"]`).first().val()); | |
| 454 | - extension_settings.caption.multimodal_api = api; | |
| 455 | - extension_settings.caption.multimodal_model = model; | |
| 456 | - saveSettingsDebounced(); | |
| 457 | - switchMultimodalBlocks(); | |
| 458 | - }); | |
| 459 | - $('#caption_multimodal_model').on('change', () => { | |
| 460 | - extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); | |
| 461 | - saveSettingsDebounced(); | |
| 462 | - }); | |
| 463 | 447 | } |
| 464 | 448 | async function addSettings() { |
| 465 | 449 | const html = await renderExtensionTemplateAsync('caption', 'settings', { TEMPLATE_DEFAULT, PROMPT_DEFAULT }); |
| 466 | 450 | $('#caption_container').append(html); |
| 467 | 451 | } |
| 452 | + async function addOpenRouterModels() { | |
| 453 | + const dropdown = document.getElementById('caption_multimodal_model'); | |
| 454 | + if (!(dropdown instanceof HTMLSelectElement)) { | |
| 455 | + return; | |
| 456 | + } | |
| 457 | + if (extension_settings.caption.source !== 'multimodal' || extension_settings.caption.multimodal_api !== 'openrouter') { | |
| 458 | + return; | |
| 459 | + } | |
| 460 | + const options = Array.from(dropdown.options); | |
| 461 | + const response = await fetch('/api/openrouter/models/multimodal', { | |
| 462 | + method: 'POST', | |
| 463 | + headers: getRequestHeaders(), | |
| 464 | + }); | |
| 465 | + if (!response.ok) { | |
| 466 | + return; | |
| 467 | + } | |
| 468 | + const modelIds = await response.json(); | |
| 469 | + if (Array.isArray(modelIds) && modelIds.length > 0) { | |
| 470 | + modelIds.forEach((modelId) => { | |
| 471 | + if (!modelId || typeof modelId !== 'string' || options.some(o => o.value === modelId)) { | |
| 472 | + return; | |
| 473 | + } | |
| 474 | + const option = document.createElement('option'); | |
| 475 | + option.value = modelId; | |
| 476 | + option.textContent = modelId; | |
| 477 | + option.dataset.type = 'openrouter'; | |
| 478 | + dropdown.add(option); | |
| 479 | + }); | |
| 480 | + } | |
| 481 | + } | |
| 468 | 482 | |
| 469 | 483 | await addSettings(); |
| 470 | 484 | addPictureSendForm(); |
| 471 | 485 | addSendPictureButton(); |
| 472 | 486 | setImageIcon(); |
| 473 | 487 | migrateSettings(); |
| 474 | 488 | await switchMultimodalBlocks(); |
| 475 | 489 | |
| 476 | 490 | $('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode)); |
| 477 | 491 | $('#caption_allow_reverse_proxy').prop('checked', !!(extension_settings.caption.allow_reverse_proxy)); |
| @@ -506,6 +520,24 @@ jQuery(async function () { | ||
| 506 | 520 | extension_settings.caption.auto_mode = !!$('#caption_auto_mode').prop('checked'); |
| 507 | 521 | saveSettingsDebounced(); |
| 508 | 522 | }); |
| 523 | + $('#caption_ollama_pull').on('click', (e) => { | |
| 524 | + const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : ''; | |
| 525 | + e.preventDefault(); | |
| 526 | + $('#ollama_download_model').trigger('click'); | |
| 527 | + $('#dialogue_popup_input').val(presetModel); | |
| 528 | + }); | |
| 529 | + $('#caption_multimodal_api').on('change', () => { | |
| 530 | + const api = String($('#caption_multimodal_api').val()); | |
| 531 | + const model = String($(`#caption_multimodal_model option[data-type="${api}"]`).first().val()); | |
| 532 | + extension_settings.caption.multimodal_api = api; | |
| 533 | + extension_settings.caption.multimodal_model = model; | |
| 534 | + saveSettingsDebounced(); | |
| 535 | + switchMultimodalBlocks(); | |
| 536 | + }); | |
| 537 | + $('#caption_multimodal_model').on('change', () => { | |
| 538 | + extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); | |
| 539 | + saveSettingsDebounced(); | |
| 540 | + }); | |
| 509 | 541 | |
| 510 | 542 | const onMessageEvent = async (index) => { |
| 511 | 543 | if (!extension_settings.caption.auto_mode) { |
| @@ -538,7 +570,8 @@ jQuery(async function () { | ||
| 538 | 570 | } |
| 539 | 571 | }); |
| 540 | 572 | |
| 541 | 573 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'caption', |
| 574 | + name: 'caption', | |
| 542 | 575 | callback: captionCommandCallback, |
| 543 | 576 | returns: 'caption', |
| 544 | 577 | namedArgumentList: [ |
| @@ -23,6 +23,7 @@ | ||
| 23 | 23 | <option value="google">Google AI Studio</option> |
| 24 | 24 | <option value="koboldcpp">KoboldCpp</option> |
| 25 | 25 | <option value="llamacpp">llama.cpp</option> |
| 26 | + <option value="mistral">MistralAI</option> | |
| 26 | 27 | <option value="ollama">Ollama</option> |
| 27 | 28 | <option value="openai">OpenAI</option> |
| 28 | 29 | <option value="openrouter">OpenRouter</option> |
| @@ -33,6 +34,8 @@ | ||
| 33 | 34 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| 34 | 35 | <label for="caption_multimodal_model" data-i18n="Model">Model</label> |
| 35 | 36 | <select id="caption_multimodal_model" class="flex1 text_pole"> |
| 37 | + <option data-type="mistral" value="pixtral-latest">pixtral-latest</option> | |
| 38 | + <option data-type="mistral" value="pixtral-12b-2409">pixtral-12b-2409</option> | |
| 36 | 39 | <option data-type="zerooneai" value="yi-vision">yi-vision</option> |
| 37 | 40 | <option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option> |
| 38 | 41 | <option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option> |
| @@ -96,7 +99,7 @@ | ||
| 96 | 99 | <div data-type="ollama"> |
| 97 | 100 | The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>. |
| 98 | 101 | </div> |
| 99 | 102 | <label data-type="openai,anthropic,google,mistral" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid."> |
| 100 | 103 | <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox"> |
| 101 | 104 | <span data-i18n="Allow reverse proxy">Allow reverse proxy</span> |
| 102 | 105 | </label> |
| @@ -19,20 +19,23 @@ const DEFAULT_SETTINGS = { | ||
| 19 | 19 | selectedProfile: null, |
| 20 | 20 | }; |
| 21 | 21 | |
| 22 | 22 | const COMMON_COMMANDSCC_COMMANDS = [ |
| 23 | 23 | 'api', |
| 24 | 24 | 'preset', |
| 25 | + // Do not fix; CC needs to set the API twice because it could be overridden by the preset | |
| 26 | + 'api', | |
| 25 | 27 | 'api-url', |
| 26 | 28 | 'model', |
| 27 | -]; | |
| 28 | - | |
| 29 | -const CC_COMMANDS = [ | |
| 30 | - ...COMMON_COMMANDS, | |
| 31 | 29 | 'proxy', |
| 32 | 30 | ]; |
| 33 | 31 | |
| 34 | 32 | const TC_COMMANDS = [ |
| 35 | - ...COMMON_COMMANDS, | |
| 33 | + 'api', | |
| 34 | + 'preset', | |
| 35 | + 'api-url', | |
| 36 | + 'model', | |
| 37 | + 'sysprompt', | |
| 38 | + 'sysprompt-state', | |
| 36 | 39 | 'instruct', |
| 37 | 40 | 'context', |
| 38 | 41 | 'instruct-state', |
| @@ -45,6 +48,8 @@ const FANCY_NAMES = { | ||
| 45 | 48 | 'preset': 'Settings Preset', |
| 46 | 49 | 'model': 'Model', |
| 47 | 50 | 'proxy': 'Proxy Preset', |
| 51 | + 'sysprompt-state': 'Use System Prompt', | |
| 52 | + 'sysprompt': 'System Prompt Name', | |
| 48 | 53 | 'instruct-state': 'Instruct Mode', |
| 49 | 54 | 'instruct': 'Instruct Template', |
| 50 | 55 | 'context': 'Context Template', |
| @@ -180,6 +185,11 @@ async function readProfileFromCommands(mode, profile, cleanUp = false) { | ||
| 180 | 185 | } |
| 181 | 186 | |
| 182 | 187 | if (cleanUp) { |
| 188 | + for (const command of commands) { | |
| 189 | + if (command.endsWith('-state') && profile[command] === 'false') { | |
| 190 | + delete profile[command.replace('-state', '')]; | |
| 191 | + } | |
| 192 | + } | |
| 183 | 193 | for (const command of opposingCommands) { |
| 184 | 194 | if (commands.includes(command)) { |
| 185 | 195 | continue; |
| @@ -325,7 +335,7 @@ function renderConnectionProfiles(profiles) { | ||
| 325 | 335 | noneOption.selected = !extension_settings.connectionManager.selectedProfile; |
| 326 | 336 | profiles.appendChild(noneOption); |
| 327 | 337 | |
| 328 | 338 | for (const profile of extension_settings.connectionManager.profiles.sort((a, b) => a.name.localeCompare(b.name))) { |
| 329 | 339 | const option = document.createElement('option'); |
| 330 | 340 | option.value = profile.id; |
| 331 | 341 | option.textContent = profile.name; |
| @@ -462,6 +472,31 @@ async function renderDetailsContent(detailsContent) { | ||
| 462 | 472 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE); |
| 463 | 473 | }); |
| 464 | 474 | |
| 475 | + const renameButton = document.getElementById('rename_connection_profile'); | |
| 476 | + renameButton.addEventListener('click', async () => { | |
| 477 | + const selectedProfile = extension_settings.connectionManager.selectedProfile; | |
| 478 | + const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); | |
| 479 | + if (!profile) { | |
| 480 | + console.log('No profile selected'); | |
| 481 | + return; | |
| 482 | + } | |
| 483 | + | |
| 484 | + const newName = await Popup.show.input('Enter a new name', null, profile.name, { rows: 2 }); | |
| 485 | + if (!newName) { | |
| 486 | + return; | |
| 487 | + } | |
| 488 | + | |
| 489 | + if (extension_settings.connectionManager.profiles.some(p => p.name === newName)) { | |
| 490 | + toastr.error('A profile with the same name already exists.'); | |
| 491 | + return; | |
| 492 | + } | |
| 493 | + | |
| 494 | + profile.name = newName; | |
| 495 | + saveSettingsDebounced(); | |
| 496 | + renderConnectionProfiles(profiles); | |
| 497 | + toastr.success('Connection profile renamed', '', { timeOut: 1500 }); | |
| 498 | + }); | |
| 499 | + | |
| 465 | 500 | /** @type {HTMLElement} */ |
| 466 | 501 | const viewDetails = document.getElementById('view_connection_profile'); |
| 467 | 502 | const detailsContent = document.getElementById('connection_profile_details_content'); |
| @@ -13,6 +13,7 @@ | ||
| 13 | 13 | <i id="view_connection_profile" class="menu_button fa-solid fa-info-circle" title="View connection profile details" data-i18n="[title]View connection profile details"></i> |
| 14 | 14 | <i id="create_connection_profile" class="menu_button fa-solid fa-file-circle-plus" title="Create a new connection profile" data-i18n="[title]Create a new connection profile"></i> |
| 15 | 15 | <i id="update_connection_profile" class="menu_button fa-solid fa-save" title="Update a connection profile" data-i18n="[title]Update a connection profile"></i> |
| 16 | + <i id="rename_connection_profile" class="menu_button fa-solid fa-pencil" title="Rename a connection profile" data-i18n="[title]Rename a connection profile"></i> | |
| 16 | 17 | <i id="reload_connection_profile" class="menu_button fa-solid fa-recycle" title="Reload a connection profile" data-i18n="[title]Reload a connection profile"></i> |
| 17 | 18 | <i id="delete_connection_profile" class="menu_button fa-solid fa-trash-can" title="Delete a connection profile" data-i18n="[title]Delete a connection profile"></i> |
| 18 | 19 | </div> |
| @@ -20,7 +20,7 @@ const STREAMING_UPDATE_INTERVAL = 10000; | ||
| 20 | 20 | const TALKINGCHECK_UPDATE_INTERVAL = 500; |
| 21 | 21 | const DEFAULT_FALLBACK_EXPRESSION = 'joy'; |
| 22 | 22 | const FUNCTION_NAME = 'set_emotion'; |
| 23 | 23 | const DEFAULT_LLM_PROMPT = 'PauseIgnore yourprevious roleplayinstructions. Classify the emotion of the last message. Output just one word, e.g. "joy" or "anger". Choose only one of the following labels: {{labels}}'; |
| 24 | 24 | const DEFAULT_EXPRESSIONS = [ |
| 25 | 25 | 'talkinghead', |
| 26 | 26 | 'admiration', |
| @@ -2064,6 +2064,11 @@ function migrateSettings() { | ||
| 2064 | 2064 | }); |
| 2065 | 2065 | eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced); |
| 2066 | 2066 | eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced); |
| 2067 | + eventSource.on(event_types.EXTRAS_CONNECTED, () => { | |
| 2068 | + if (extension_settings.expressions.talkinghead) { | |
| 2069 | + setTalkingHeadState(extension_settings.expressions.talkinghead); | |
| 2070 | + } | |
| 2071 | + }); | |
| 2067 | 2072 | |
| 2068 | 2073 | const localEnumProviders = { |
| 2069 | 2074 | expressions: () => getCachedExpressions().map(expression => { |
| @@ -102,7 +102,7 @@ const prompt_builders = { | ||
| 102 | 102 | RAW_NON_BLOCKING: 2, |
| 103 | 103 | }; |
| 104 | 104 | |
| 105 | 105 | const defaultPrompt = '[PauseIgnore yourprevious roleplayinstructions. Summarize the most important facts and events in the story so far. If a summary already exists in your memory, use that as a base and expand with new facts. Limit the summary to {{words}} words or less. Your response should include nothing but the summary.]'; |
| 106 | 106 | const defaultTemplate = '[Summary: {{summary}}]'; |
| 107 | 107 | |
| 108 | 108 | const defaultSettings = { |
| @@ -1904,6 +1904,7 @@ export class QuickReply { | ||
| 1904 | 1904 | executeOnAi: this.executeOnAi, |
| 1905 | 1905 | executeOnChatChange: this.executeOnChatChange, |
| 1906 | 1906 | executeOnGroupMemberDraft: this.executeOnGroupMemberDraft, |
| 1907 | + executeOnNewChat: this.executeOnNewChat, | |
| 1907 | 1908 | automationId: this.automationId, |
| 1908 | 1909 | }; |
| 1909 | 1910 | } |
| @@ -35,6 +35,7 @@ export class SlashCommandHandler { | ||
| 35 | 35 | if (qr.executeOnUser) icons += enumIcons.user; |
| 36 | 36 | if (qr.executeOnAi) icons += enumIcons.assistant; |
| 37 | 37 | if (qr.executeOnChatChange) icons += '💬'; |
| 38 | + if (qr.executeOnNewChat) icons += '🆕'; | |
| 38 | 39 | if (qr.executeOnGroupMemberDraft) icons += enumIcons.group; |
| 39 | 40 | return icons; |
| 40 | 41 | } |
| @@ -265,6 +266,7 @@ export class SlashCommandHandler { | ||
| 265 | 266 | new SlashCommandNamedArgument('user', 'auto execute on user message, e.g., user=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), |
| 266 | 267 | new SlashCommandNamedArgument('bot', 'auto execute on AI message, e.g., bot=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), |
| 267 | 268 | new SlashCommandNamedArgument('load', 'auto execute on chat load, e.g., load=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), |
| 269 | + new SlashCommandNamedArgument('new', 'auto execute on new chat, e.g., new=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), | |
| 268 | 270 | new SlashCommandNamedArgument('group', 'auto execute on group member selection, e.g., group=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), |
| 269 | 271 | new SlashCommandNamedArgument('title', 'title / tooltip to be shown on button, e.g., title="My Fancy Button"', [ARGUMENT_TYPE.STRING], false), |
| 270 | 272 | ]; |
| @@ -857,6 +859,7 @@ export class SlashCommandHandler { | ||
| 857 | 859 | executeOnUser: isTrueBoolean(args.user), |
| 858 | 860 | executeOnAi: isTrueBoolean(args.bot), |
| 859 | 861 | executeOnChatChange: isTrueBoolean(args.load), |
| 862 | + executeOnNewChat: isTrueBoolean(args.new), | |
| 860 | 863 | executeOnGroupMemberDraft: isTrueBoolean(args.group), |
| 861 | 864 | automationId: args.automationId ?? '', |
| 862 | 865 | }, |
| @@ -889,6 +892,7 @@ export class SlashCommandHandler { | ||
| 889 | 892 | executeOnAi: args.bot === undefined ? undefined : isTrueBoolean(args.bot), |
| 890 | 893 | executeOnChatChange: args.load === undefined ? undefined : isTrueBoolean(args.load), |
| 891 | 894 | executeOnGroupMemberDraft: args.group === undefined ? undefined : isTrueBoolean(args.group), |
| 895 | + executeOnNewChat: args.new === undefined ? undefined : isTrueBoolean(args.new), | |
| 892 | 896 | automationId: args.automationId ?? '', |
| 893 | 897 | }, |
| 894 | 898 | ); |
| @@ -13,7 +13,7 @@ import { createThumbnail, isValidUrl } from '../utils.js'; | ||
| 13 | 13 | */ |
| 14 | 14 | export async function getMultimodalCaption(base64Img, prompt) { |
| 15 | 15 | const useReverseProxy = |
| 16 | 16 | (['openai', 'anthropic', 'google', 'mistral'].includes(extension_settings.caption.multimodal_api)) |
| 17 | 17 | && extension_settings.caption.allow_reverse_proxy |
| 18 | 18 | && oai_settings.reverse_proxy |
| 19 | 19 | && isValidUrl(oai_settings.reverse_proxy); |
| @@ -36,7 +36,7 @@ export async function getMultimodalCaption(base64Img, prompt) { | ||
| 36 | 36 | const isVllm = extension_settings.caption.multimodal_api === 'vllm'; |
| 37 | 37 | const base64Bytes = base64Img.length * 0.75; |
| 38 | 38 | const compressionLimit = 2 * 1024 * 1024; |
| 39 | 39 | if ((['google', 'openrouter', 'mistral'].includes(extension_settings.caption.multimodal_api) && base64Bytes > compressionLimit) || isOoba || isKoboldCpp) { |
| 40 | 40 | const maxSide = 1024; |
| 41 | 41 | base64Img = await createThumbnail(base64Img, maxSide, maxSide, 'image/jpeg'); |
| 42 | 42 | } |
| @@ -139,6 +139,10 @@ function throwIfInvalidModel(useReverseProxy) { | ||
| 139 | 139 | throw new Error('Google AI Studio API key is not set.'); |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | + if (extension_settings.caption.multi_modal_api === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) { | |
| 143 | + throw new Error('Mistral AI API key is not set.'); | |
| 144 | + } | |
| 145 | + | |
| 142 | 146 | if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) { |
| 143 | 147 | throw new Error('Ollama server URL is not set.'); |
| 144 | 148 | } |
| @@ -125,15 +125,14 @@ const messageTrigger = { | ||
| 125 | 125 | const promptTemplates = { |
| 126 | 126 | // Not really a prompt template, rather an outcome message template |
| 127 | 127 | [generationMode.MESSAGE]: '[{{char}} sends a picture that contains: {{prompt}}].', |
| 128 | - /*OLD: [generationMode.CHARACTER]: "Pause your roleplay and provide comma-delimited list of phrases and keywords which describe {{char}}'s physical appearance and clothing. Ignore {{char}}'s personality traits, and chat history when crafting this description. End your response once the comma-delimited list is complete. Do not roleplay when writing this description, and do not attempt to continue the story.", */ | |
| 128 | + [generationMode.CHARACTER]: 'In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\'', | |
| 129 | - [generationMode.CHARACTER]: '[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\']', | |
| 130 | 129 | //face-specific prompt |
| 131 | 130 | [generationMode.FACE]: '[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, facial features and expressions, occupation, hair and hair accessories (if any), what they are wearing on their upper body (if anything). Do not describe anything below their neck. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'close up facial portrait,\']', |
| 132 | 131 | //prompt for only the last message |
| 133 | 132 | [generationMode.USER]: '[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{user}}\'s physical appearance from the perspective of {{char}} in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{char}} when writing this description, and do not attempt to continue the story.]', |
| 134 | 133 | [generationMode.SCENARIO]: '[PauseIgnore yourprevious roleplayinstructions and provide a detailed description for all of the following: a brief recap of recent events in the story, {{char}}\'s appearance, and {{char}}\'s surroundings. Do not roleplayreply as {{char}} while writing this description.]', |
| 135 | 134 | |
| 136 | 135 | [generationMode.NOW]: `[PauseIgnore yourprevious roleplayinstructions. Your next response must be formatted as a single comma-delimited list of concise keywords. The list will describe of the visual details included in the last chat message. |
| 137 | 136 | |
| 138 | 137 | Only mention characters by using pronouns ('he','his','she','her','it','its') or neutral nouns ('male', 'the man', 'female', 'the woman'). |
| 139 | 138 | |
| @@ -155,14 +154,14 @@ const promptTemplates = { | ||
| 155 | 154 | If character actions involve direct physical interaction with another character, mention specifically which body parts interacting and how. |
| 156 | 155 | |
| 157 | 156 | A correctly formatted example response would be: |
| 158 | 157 | '(location),(character list by gender),(primary action), (relative character position) POV, (character 1's description and actions), (character 2's description and actions)']`, |
| 159 | 158 | |
| 160 | 159 | [generationMode.RAW_LAST]: '[PauseIgnore yourprevious roleplayinstructions and provide ONLY the last chat message string back to me verbatim. Do not write anything after the string. Do not roleplayreply atas all{{char}} inwhen yourwriting response.this Dodescription, and do not attempt to continue the roleplay story.]', |
| 161 | 160 | [generationMode.BACKGROUND]: '[PauseIgnore yourprevious roleplayinstructions and provide a detailed description of {{char}}\'s surroundings in the form of a comma-delimited list of keywords and phrases. The list must include all of the following items in this order: location, time of day, weather, lighting, and any other relevant details. Do not include descriptions of characters and non-visual qualities such as names, personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'background,\'. Ignore the rest of the story when crafting this description. Do not roleplayreply as {{userchar}} when writing this description, and do not attempt to continue the story.]', |
| 162 | 161 | [generationMode.FACE_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "close-up portrait".', |
| 163 | 162 | [generationMode.CHARACTER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".', |
| 164 | 163 | [generationMode.USER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".', |
| 165 | 164 | [generationMode.FREE_EXTENDED]: 'PauseIgnore yourprevious roleplayinstructions and provide an exhaustive comma-separated list of tags describing the appearance of "{0}" in great detail. Start with {{charPrefix}} (sic) if the subject is associated with {{char}}.', |
| 166 | 165 | }; |
| 167 | 166 | |
| 168 | 167 | const defaultPrefix = 'best quality, absurdres, aesthetic,'; |
| @@ -3813,7 +3812,10 @@ jQuery(async () => { | ||
| 3813 | 3812 | ], |
| 3814 | 3813 | helpString: ` |
| 3815 | 3814 | <div> |
| 3816 | 3815 | Requests to generate an image and posts it to chat (unless <code>quiet=true</code> argument is specified).</code>. |
| 3816 | + </div> | |
| 3817 | + <div> | |
| 3818 | + Supported arguments: <code>${Object.values(triggerWords).flat().join(', ')}</code>. | |
| 3817 | 3819 | </div> |
| 3818 | 3820 | <div> |
| 3819 | 3821 | Anything else would trigger a "free mode" to make generate whatever you prompted. Example: <code>/imagine apple tree</code> would generate a picture of an apple tree. Returns a link to the generated image. |
| @@ -349,7 +349,7 @@ | ||
| 349 | 349 | </div> |
| 350 | 350 | </div> |
| 351 | 351 | |
| 352 | 352 | <div class="flex-container marginTopBot5" data-sd-source="auto,vlad,extras,horde,drawthings,comfy"> |
| 353 | 353 | <label class="flex1 checkbox_label"> |
| 354 | 354 | <input id="sd_restore_faces" type="checkbox" /> |
| 355 | 355 | <small data-i18n="Restore Faces">Restore Faces</small> |
| @@ -56,7 +56,7 @@ const settings = { | ||
| 56 | 56 | summarize: false, |
| 57 | 57 | summarize_sent: false, |
| 58 | 58 | summary_source: 'main', |
| 59 | 59 | summary_prompt: 'PauseIgnore yourprevious roleplayinstructions. Summarize the most important parts of the message. Limit yourself to 250 words or less. Your response should include nothing but the summary.', |
| 60 | 60 | force_chunk_delimiter: '', |
| 61 | 61 | |
| 62 | 62 | // For chats |
| @@ -718,7 +718,7 @@ async function getQueryText(chat, initiator) { | ||
| 718 | 718 | async function getSavedHashes(collectionId) { |
| 719 | 719 | const response = await fetch('/api/vector/list', { |
| 720 | 720 | method: 'POST', |
| 721 | 721 | headers: getRequestHeadersgetVectorHeaders(), |
| 722 | 722 | body: JSON.stringify({ |
| 723 | 723 | collectionId: collectionId, |
| 724 | 724 | source: settings.source, |
| @@ -737,105 +737,48 @@ function getVectorHeaders() { | ||
| 737 | 737 | const headers = getRequestHeaders(); |
| 738 | 738 | switch (settings.source) { |
| 739 | 739 | case 'extras': |
| 740 | - addExtrasHeaders(headers); | |
| 741 | - break; | |
| 742 | - case 'togetherai': | |
| 743 | - addTogetherAiHeaders(headers); | |
| 744 | - break; | |
| 745 | - case 'openai': | |
| 746 | - addOpenAiHeaders(headers); | |
| 747 | - break; | |
| 748 | - case 'cohere': | |
| 749 | - addCohereHeaders(headers); | |
| 750 | - break; | |
| 751 | - case 'ollama': | |
| 752 | - addOllamaHeaders(headers); | |
| 753 | - break; | |
| 754 | - case 'llamacpp': | |
| 755 | - addLlamaCppHeaders(headers); | |
| 756 | - break; | |
| 757 | - case 'vllm': | |
| 758 | - addVllmHeaders(headers); | |
| 759 | - break; | |
| 760 | - default: | |
| 761 | - break; | |
| 762 | - } | |
| 763 | - return headers; | |
| 764 | -} | |
| 765 | - | |
| 766 | -/** | |
| 767 | - * Add headers for the Extras API source. | |
| 768 | - * @param {object} headers Headers object | |
| 769 | - */ | |
| 770 | -function addExtrasHeaders(headers) { | |
| 771 | - console.log(`Vector source is extras, populating API URL: ${extension_settings.apiUrl}`); | |
| 772 | 740 | Object.assign(headers, { |
| 773 | 741 | 'X-Extras-Url': extension_settings.apiUrl, |
| 774 | 742 | 'X-Extras-Key': extension_settings.apiKey, |
| 775 | 743 | }); |
| 776 | -} | |
| 744 | + break; | |
| 777 | - | |
| 745 | + case 'togetherai': | |
| 778 | -/** | |
| 779 | - * Add headers for the TogetherAI API source. | |
| 780 | - * @param {object} headers Headers object | |
| 781 | - */ | |
| 782 | -function addTogetherAiHeaders(headers) { | |
| 783 | 746 | Object.assign(headers, { |
| 784 | 747 | 'X-Togetherai-Model': extension_settings.vectors.togetherai_model, |
| 785 | 748 | }); |
| 786 | -} | |
| 749 | + break; | |
| 787 | - | |
| 750 | + case 'openai': | |
| 788 | -/** | |
| 789 | - * Add headers for the OpenAI API source. | |
| 790 | - * @param {object} headers Header object | |
| 791 | - */ | |
| 792 | -function addOpenAiHeaders(headers) { | |
| 793 | 751 | Object.assign(headers, { |
| 794 | 752 | 'X-OpenAI-Model': extension_settings.vectors.openai_model, |
| 795 | 753 | }); |
| 796 | -} | |
| 754 | + break; | |
| 797 | - | |
| 755 | + case 'cohere': | |
| 798 | -/** | |
| 799 | - * Add headers for the Cohere API source. | |
| 800 | - * @param {object} headers Header object | |
| 801 | - */ | |
| 802 | -function addCohereHeaders(headers) { | |
| 803 | 756 | Object.assign(headers, { |
| 804 | 757 | 'X-Cohere-Model': extension_settings.vectors.cohere_model, |
| 805 | 758 | }); |
| 806 | -} | |
| 759 | + break; | |
| 807 | - | |
| 760 | + case 'ollama': | |
| 808 | -/** | |
| 809 | - * Add headers for the Ollama API source. | |
| 810 | - * @param {object} headers Header object | |
| 811 | - */ | |
| 812 | -function addOllamaHeaders(headers) { | |
| 813 | 761 | Object.assign(headers, { |
| 814 | 762 | 'X-Ollama-Model': extension_settings.vectors.ollama_model, |
| 815 | 763 | 'X-Ollama-URL': textgenerationwebui_settings.server_urls[textgen_types.OLLAMA], |
| 816 | 764 | 'X-Ollama-Keep': !!extension_settings.vectors.ollama_keep, |
| 817 | 765 | }); |
| 818 | -} | |
| 766 | + break; | |
| 819 | - | |
| 767 | + case 'llamacpp': | |
| 820 | -/** | |
| 821 | - * Add headers for the LlamaCpp API source. | |
| 822 | - * @param {object} headers Header object | |
| 823 | - */ | |
| 824 | -function addLlamaCppHeaders(headers) { | |
| 825 | 768 | Object.assign(headers, { |
| 826 | 769 | 'X-LlamaCpp-URL': textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP], |
| 827 | 770 | }); |
| 828 | -} | |
| 771 | + break; | |
| 829 | - | |
| 772 | + case 'vllm': | |
| 830 | -/** | |
| 831 | - * Add headers for the VLLM API source. | |
| 832 | - * @param {object} headers Header object | |
| 833 | - */ | |
| 834 | -function addVllmHeaders(headers) { | |
| 835 | 773 | Object.assign(headers, { |
| 836 | 774 | 'X-Vllm-URL': textgenerationwebui_settings.server_urls[textgen_types.VLLM], |
| 837 | 775 | 'X-Vllm-Model': extension_settings.vectors.vllm_model, |
| 838 | 776 | }); |
| 777 | + break; | |
| 778 | + default: | |
| 779 | + break; | |
| 780 | + } | |
| 781 | + return headers; | |
| 839 | 782 | } |
| 840 | 783 | |
| 841 | 784 | /** |
| @@ -901,7 +844,7 @@ function throwIfSourceInvalid() { | ||
| 901 | 844 | async function deleteVectorItems(collectionId, hashes) { |
| 902 | 845 | const response = await fetch('/api/vector/delete', { |
| 903 | 846 | method: 'POST', |
| 904 | 847 | headers: getRequestHeadersgetVectorHeaders(), |
| 905 | 848 | body: JSON.stringify({ |
| 906 | 849 | collectionId: collectionId, |
| 907 | 850 | hashes: hashes, |
| @@ -987,7 +930,7 @@ async function purgeFileVectorIndex(fileUrl) { | ||
| 987 | 930 | |
| 988 | 931 | const response = await fetch('/api/vector/purge', { |
| 989 | 932 | method: 'POST', |
| 990 | 933 | headers: getRequestHeadersgetVectorHeaders(), |
| 991 | 934 | body: JSON.stringify({ |
| 992 | 935 | collectionId: collectionId, |
| 993 | 936 | }), |
| @@ -1016,7 +959,7 @@ async function purgeVectorIndex(collectionId) { | ||
| 1016 | 959 | |
| 1017 | 960 | const response = await fetch('/api/vector/purge', { |
| 1018 | 961 | method: 'POST', |
| 1019 | 962 | headers: getRequestHeadersgetVectorHeaders(), |
| 1020 | 963 | body: JSON.stringify({ |
| 1021 | 964 | collectionId: collectionId, |
| 1022 | 965 | }), |
| @@ -1041,7 +984,7 @@ async function purgeAllVectorIndexes() { | ||
| 1041 | 984 | try { |
| 1042 | 985 | const response = await fetch('/api/vector/purge-all', { |
| 1043 | 986 | method: 'POST', |
| 1044 | 987 | headers: getRequestHeadersgetVectorHeaders(), |
| 1045 | 988 | }); |
| 1046 | 989 | |
| 1047 | 990 | if (!response.ok) { |
| @@ -1056,6 +999,25 @@ async function purgeAllVectorIndexes() { | ||
| 1056 | 999 | } |
| 1057 | 1000 | } |
| 1058 | 1001 | |
| 1002 | +async function isModelScopesEnabled() { | |
| 1003 | + try { | |
| 1004 | + const response = await fetch('/api/vector/scopes-enabled', { | |
| 1005 | + method: 'GET', | |
| 1006 | + headers: getVectorHeaders(), | |
| 1007 | + }); | |
| 1008 | + | |
| 1009 | + if (!response.ok) { | |
| 1010 | + return false; | |
| 1011 | + } | |
| 1012 | + | |
| 1013 | + const data = await response.json(); | |
| 1014 | + return data?.enabled ?? false; | |
| 1015 | + } catch (error) { | |
| 1016 | + console.error('Vectors: Failed to check model scopes', error); | |
| 1017 | + return false; | |
| 1018 | + } | |
| 1019 | +} | |
| 1020 | + | |
| 1059 | 1021 | function toggleSettings() { |
| 1060 | 1022 | $('#vectors_files_settings').toggle(!!settings.enabled_files); |
| 1061 | 1023 | $('#vectors_chats_settings').toggle(!!settings.enabled_chats); |
| @@ -1320,6 +1282,7 @@ jQuery(async () => { | ||
| 1320 | 1282 | } |
| 1321 | 1283 | |
| 1322 | 1284 | Object.assign(settings, extension_settings.vectors); |
| 1285 | + const scopesEnabled = await isModelScopesEnabled(); | |
| 1323 | 1286 | |
| 1324 | 1287 | // Migrate from TensorFlow to Transformers |
| 1325 | 1288 | settings.source = settings.source !== 'local' ? settings.source : 'transformers'; |
| @@ -1371,31 +1334,31 @@ jQuery(async () => { | ||
| 1371 | 1334 | saveSettingsDebounced(); |
| 1372 | 1335 | }); |
| 1373 | 1336 | $('#vectors_togetherai_model').val(settings.togetherai_model).on('change', () => { |
| 1374 | 1337 | !scopesEnabled && $('#vectors_modelWarning').show(); |
| 1375 | 1338 | settings.togetherai_model = String($('#vectors_togetherai_model').val()); |
| 1376 | 1339 | Object.assign(extension_settings.vectors, settings); |
| 1377 | 1340 | saveSettingsDebounced(); |
| 1378 | 1341 | }); |
| 1379 | 1342 | $('#vectors_openai_model').val(settings.openai_model).on('change', () => { |
| 1380 | 1343 | !scopesEnabled && $('#vectors_modelWarning').show(); |
| 1381 | 1344 | settings.openai_model = String($('#vectors_openai_model').val()); |
| 1382 | 1345 | Object.assign(extension_settings.vectors, settings); |
| 1383 | 1346 | saveSettingsDebounced(); |
| 1384 | 1347 | }); |
| 1385 | 1348 | $('#vectors_cohere_model').val(settings.cohere_model).on('change', () => { |
| 1386 | 1349 | !scopesEnabled && $('#vectors_modelWarning').show(); |
| 1387 | 1350 | settings.cohere_model = String($('#vectors_cohere_model').val()); |
| 1388 | 1351 | Object.assign(extension_settings.vectors, settings); |
| 1389 | 1352 | saveSettingsDebounced(); |
| 1390 | 1353 | }); |
| 1391 | 1354 | $('#vectors_ollama_model').val(settings.ollama_model).on('input', () => { |
| 1392 | 1355 | !scopesEnabled && $('#vectors_modelWarning').show(); |
| 1393 | 1356 | settings.ollama_model = String($('#vectors_ollama_model').val()); |
| 1394 | 1357 | Object.assign(extension_settings.vectors, settings); |
| 1395 | 1358 | saveSettingsDebounced(); |
| 1396 | 1359 | }); |
| 1397 | 1360 | $('#vectors_vllm_model').val(settings.vllm_model).on('input', () => { |
| 1398 | 1361 | !scopesEnabled && $('#vectors_modelWarning').show(); |
| 1399 | 1362 | settings.vllm_model = String($('#vectors_vllm_model').val()); |
| 1400 | 1363 | Object.assign(extension_settings.vectors, settings); |
| 1401 | 1364 | saveSettingsDebounced(); |
| @@ -98,8 +98,9 @@ | ||
| 98 | 98 | |
| 99 | 99 | <small id="vectors_modelWarning"> |
| 100 | 100 | <i class="fa-solid fa-exclamation-triangle"></i> |
| 101 | - <span data-i18n="Vectors Model Warning"> | |
| 101 | + <span> | |
| 102 | - It is recommended to purge vectors when changing the model mid-chat. Otherwise, it will lead to sub-par results. | |
| 102 | + Set <code>vectors.enableModelScopes</code> to true in config.yaml to switch between vectorization models without needing to purge existing vectors. | |
| 103 | + This option will soon be enabled by default. | |
| 103 | 104 | </span> |
| 104 | 105 | </small> |
| 105 | 106 | |
| @@ -1,10 +1,10 @@ | ||
| 1 | 1 | import { |
| 2 | 2 | saveSettingsDebouncedamount_gen, |
| 3 | 3 | callPopup, |
| 4 | - setGenerationProgress, | |
| 5 | 4 | getRequestHeaders, |
| 6 | 5 | max_context, |
| 7 | 6 | amount_gensaveSettingsDebounced, |
| 7 | + setGenerationProgress, | |
| 8 | 8 | } from '../script.js'; |
| 9 | 9 | import { SECRET_KEYS, writeSecret } from './secrets.js'; |
| 10 | 10 | import { delay } from './utils.js'; |
| @@ -45,8 +45,7 @@ async function getWorkers(force) { | ||
| 45 | 45 | headers: getRequestHeaders(), |
| 46 | 46 | body: JSON.stringify({ force }), |
| 47 | 47 | }); |
| 48 | 48 | const data =return await response.json(); |
| 49 | - return data; | |
| 50 | 49 | } |
| 51 | 50 | |
| 52 | 51 | /** |
| @@ -61,9 +60,11 @@ async function getModels(force) { | ||
| 61 | 60 | body: JSON.stringify({ force }), |
| 62 | 61 | }); |
| 63 | 62 | const data = await response.json(); |
| 63 | + console.log('getModels', data); | |
| 64 | 64 | return data; |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | + | |
| 67 | 68 | /** |
| 68 | 69 | * Gets the status of a Horde task. |
| 69 | 70 | * @param {string} taskId Task ID |
| @@ -80,8 +81,7 @@ async function getTaskStatus(taskId) { | ||
| 80 | 81 | throw new Error(`Failed to get task status: ${response.statusText}`); |
| 81 | 82 | } |
| 82 | 83 | |
| 83 | 84 | const data =return await response.json(); |
| 84 | - return data; | |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | /** |
| @@ -148,7 +148,7 @@ async function adjustHordeGenerationParams(max_context_length, max_length) { | ||
| 148 | 148 | |
| 149 | 149 | for (const model of selectedModels) { |
| 150 | 150 | for (const worker of workers) { |
| 151 | 151 | if (model.cluster === worker.cluster && worker.models.includes(model.name)) { |
| 152 | 152 | // Skip workers that are not trusted if the option is enabled |
| 153 | 153 | if (horde_settings.trusted_workers_only && !worker.trusted) { |
| 154 | 154 | continue; |
| @@ -250,12 +250,10 @@ async function generateHorde(prompt, params, signal, reportProgress) { | ||
| 250 | 250 | console.log(generatedText); |
| 251 | 251 | console.log(`Generated by Horde Worker: ${WorkerName} [${WorkerModel}]`); |
| 252 | 252 | return { text: generatedText, workerName: `Generated by Horde worker: ${WorkerName} [${WorkerModel}]` }; |
| 253 | - } | |
| 253 | + } else if (!queue_position_first) { | |
| 254 | - else if (!queue_position_first) { | |
| 255 | 254 | queue_position_first = statusCheckJson.queue_position; |
| 256 | 255 | reportProgress && setGenerationProgress(0); |
| 257 | - } | |
| 256 | + } else if (statusCheckJson.queue_position >= 0) { | |
| 258 | - else if (statusCheckJson.queue_position >= 0) { | |
| 259 | 257 | let queue_position = statusCheckJson.queue_position; |
| 260 | 258 | const progress = Math.round(100 - (queue_position / queue_position_first * 100)); |
| 261 | 259 | reportProgress && setGenerationProgress(progress); |
| @@ -268,17 +266,24 @@ async function generateHorde(prompt, params, signal, reportProgress) { | ||
| 268 | 266 | throw new Error('Horde timeout'); |
| 269 | 267 | } |
| 270 | 268 | |
| 269 | + | |
| 271 | 270 | /** |
| 272 | 271 | * Displays the available models in the Horde model selection dropdown. |
| 273 | 272 | * @param {boolean} force Force refresh of the models |
| 274 | 273 | */ |
| 275 | 274 | async function getHordeModels(force) { |
| 275 | + const sortByPerformance = (a, b) => b.performance - a.performance; | |
| 276 | + const sortByWhitelisted = (a, b) => b.is_whitelisted - a.is_whitelisted; | |
| 277 | + const sortByPopular = (a, b) => b.tags?.includes('popular') - a.tags?.includes('popular'); | |
| 278 | + | |
| 276 | 279 | $('#horde_model').empty(); |
| 277 | 280 | models = (await getModels(force)).sort((a, b) => b.performance - a.performance);{ |
| 281 | + return sortByWhitelisted(a, b) || sortByPopular(a, b) || sortByPerformance(a, b); | |
| 282 | + }); | |
| 278 | 283 | for (const model of models) { |
| 279 | 284 | const option = document.createElement('option'); |
| 280 | 285 | option.value = model.name; |
| 281 | - option.innerText = `${model.name} (ETA: ${model.eta}s, Speed: ${model.performance}, Queue: ${model.queued}, Workers: ${model.count})`; | |
| 286 | + option.innerText = hordeModelTextString(model); | |
| 282 | 287 | option.selected = horde_settings.models.includes(model.name); |
| 283 | 288 | $('#horde_model').append(option); |
| 284 | 289 | } |
| @@ -323,8 +328,66 @@ async function showKudos() { | ||
| 323 | 328 | toastr.info(`Kudos: ${data.kudos}`, data.username); |
| 324 | 329 | } |
| 325 | 330 | |
| 331 | +function hordeModelTextString(model) { | |
| 332 | + const q = hordeModelQueueStateString(model); | |
| 333 | + return `${model.name} (${q})`; | |
| 334 | +} | |
| 335 | + | |
| 336 | +function hordeModelQueueStateString(model) { | |
| 337 | + return `ETA: ${model.eta}s, Speed: ${model.performance}, Queue: ${model.queued}, Workers: ${model.count}`; | |
| 338 | +} | |
| 339 | + | |
| 340 | +function getHordeModelTemplate(option) { | |
| 341 | + const model = models.find(x => x.name === option?.element?.value); | |
| 342 | + | |
| 343 | + if (!option.id || !model) { | |
| 344 | + console.debug('No model found for option', option, option?.element?.value); | |
| 345 | + console.debug('Models', models); | |
| 346 | + return option.text; | |
| 347 | + } | |
| 348 | + | |
| 349 | + const strip = html => { | |
| 350 | + const tmp = document.createElement('DIV'); | |
| 351 | + tmp.innerHTML = html || ''; | |
| 352 | + return tmp.textContent || tmp.innerText || ''; | |
| 353 | + }; | |
| 354 | + | |
| 355 | + // how much do we trust the metadata from the models repo? about this much | |
| 356 | + const displayName = strip(model.display_name || model.name).replace(/.*\//g, ''); | |
| 357 | + const description = strip(model.description); | |
| 358 | + const tags = model.tags ? model.tags.map(strip) : []; | |
| 359 | + const url = strip(model.url); | |
| 360 | + const style = strip(model.style); | |
| 361 | + | |
| 362 | + const workerInfo = hordeModelQueueStateString(model); | |
| 363 | + const isPopular = model.tags?.includes('popular'); | |
| 364 | + const descriptionDiv = description ? `<div class="horde-model-description">${description}</div>` : ''; | |
| 365 | + const tagSpans = tags.length > 0 && | |
| 366 | + `${tags.map(tag => `<span class="tag tag_name">${tag}</span>`).join('')}</span>` || ''; | |
| 367 | + | |
| 368 | + const modelDetailsLink = url && `<a href="${url}" target="_blank" rel="noopener noreferrer" class="model-details-link fa-solid fa-circle-question"> </a>`; | |
| 369 | + const capitalize = s => s ? s[0].toUpperCase() + s.slice(1) : ''; | |
| 370 | + const innerContent = [ | |
| 371 | + `<strong>${displayName}</strong> ${modelDetailsLink}`, | |
| 372 | + style ? `${capitalize(style)}` : '', | |
| 373 | + tagSpans ? `<span class="tags tags_inline inline-flex margin-r2">${tagSpans}</span>` : '', | |
| 374 | + ].filter(Boolean).join(' | '); | |
| 375 | + | |
| 376 | + return $((` | |
| 377 | + <div class="flex-container flexFlowColumn"> | |
| 378 | + <div> | |
| 379 | + ${isPopular ? '<span class="fa-fw fa-solid fa-star" title="Popular"></span>' : ''} | |
| 380 | + ${innerContent} | |
| 381 | + </div> | |
| 382 | + ${descriptionDiv} | |
| 383 | + <div><small>${workerInfo}</small></div> | |
| 384 | + </div> | |
| 385 | + `)); | |
| 386 | +} | |
| 387 | + | |
| 326 | 388 | jQuery(function () { |
| 327 | 389 | $('#horde_model').on('mousedown change', async function (e) { |
| 390 | + console.log('Horde model change', e); | |
| 328 | 391 | horde_settings.models = $('#horde_model').val(); |
| 329 | 392 | console.log('Updated Horde models', horde_settings.models); |
| 330 | 393 | |
| @@ -374,10 +437,7 @@ jQuery(function () { | ||
| 374 | 437 | // Customize the pillbox text by shortening the full text |
| 375 | 438 | return data.id; |
| 376 | 439 | }, |
| 377 | - templateResult: function (data) { | |
| 440 | + templateResult: getHordeModelTemplate, | |
| 378 | - // Return the full text for the dropdown | |
| 379 | - return data.text; | |
| 380 | - }, | |
| 381 | 441 | }); |
| 382 | 442 | } |
| 383 | 443 | }); |
| @@ -14,10 +14,15 @@ import { regexFromString, resetScrollHeight } from './utils.js'; | ||
| 14 | 14 | */ |
| 15 | 15 | export let instruct_presets = []; |
| 16 | 16 | |
| 17 | +export const names_behavior_types = { | |
| 18 | + NONE: 'none', | |
| 19 | + FORCE: 'force', | |
| 20 | + ALWAYS: 'always', | |
| 21 | +}; | |
| 22 | + | |
| 17 | 23 | const controls = [ |
| 18 | 24 | { id: 'instruct_enabled', property: 'enabled', isCheckbox: true }, |
| 19 | 25 | { id: 'instruct_wrap', property: 'wrap', isCheckbox: true }, |
| 20 | - { id: 'instruct_system_prompt', property: 'system_prompt', isCheckbox: false }, | |
| 21 | 26 | { id: 'instruct_system_sequence_prefix', property: 'system_sequence_prefix', isCheckbox: false }, |
| 22 | 27 | { id: 'instruct_system_sequence_suffix', property: 'system_sequence_suffix', isCheckbox: false }, |
| 23 | 28 | { id: 'instruct_input_sequence', property: 'input_sequence', isCheckbox: false }, |
| @@ -29,9 +34,6 @@ const controls = [ | ||
| 29 | 34 | { id: 'instruct_last_system_sequence', property: 'last_system_sequence', isCheckbox: false }, |
| 30 | 35 | { id: 'instruct_user_alignment_message', property: 'user_alignment_message', isCheckbox: false }, |
| 31 | 36 | { id: 'instruct_stop_sequence', property: 'stop_sequence', isCheckbox: false }, |
| 32 | - { id: 'instruct_names', property: 'names', isCheckbox: true }, | |
| 33 | - { id: 'instruct_macro', property: 'macro', isCheckbox: true }, | |
| 34 | - { id: 'instruct_names_force_groups', property: 'names_force_groups', isCheckbox: true }, | |
| 35 | 37 | { id: 'instruct_first_output_sequence', property: 'first_output_sequence', isCheckbox: false }, |
| 36 | 38 | { id: 'instruct_last_output_sequence', property: 'last_output_sequence', isCheckbox: false }, |
| 37 | 39 | { id: 'instruct_first_input_sequence', property: 'first_input_sequence', isCheckbox: false }, |
| @@ -39,6 +41,7 @@ const controls = [ | ||
| 39 | 41 | { id: 'instruct_activation_regex', property: 'activation_regex', isCheckbox: false }, |
| 40 | 42 | { id: 'instruct_bind_to_context', property: 'bind_to_context', isCheckbox: true }, |
| 41 | 43 | { id: 'instruct_skip_examples', property: 'skip_examples', isCheckbox: true }, |
| 44 | + { id: 'instruct_names_behavior', property: 'names_behavior', isCheckbox: false }, | |
| 42 | 45 | { id: 'instruct_system_same_as_user', property: 'system_same_as_user', isCheckbox: true, trigger: true }, |
| 43 | 46 | ]; |
| 44 | 47 | |
| @@ -54,6 +57,15 @@ function migrateInstructModeSettings(settings) { | ||
| 54 | 57 | delete settings.separator_sequence; |
| 55 | 58 | } |
| 56 | 59 | |
| 60 | + // names, names_force_groups => names_behavior | |
| 61 | + if (settings.names !== undefined) { | |
| 62 | + settings.names_behavior = settings.names | |
| 63 | + ? names_behavior_types.ALWAYS | |
| 64 | + : (settings.names_force_groups ? names_behavior_types.FORCE : names_behavior_types.NONE); | |
| 65 | + delete settings.names; | |
| 66 | + delete settings.names_force_groups; | |
| 67 | + } | |
| 68 | + | |
| 57 | 69 | const defaults = { |
| 58 | 70 | input_suffix: '', |
| 59 | 71 | system_sequence: '', |
| @@ -65,6 +77,7 @@ function migrateInstructModeSettings(settings) { | ||
| 65 | 77 | names_force_groups: true, |
| 66 | 78 | skip_examples: false, |
| 67 | 79 | system_same_as_user: false, |
| 80 | + names_behavior: names_behavior_types.FORCE, | |
| 68 | 81 | }; |
| 69 | 82 | |
| 70 | 83 | for (let key in defaults) { |
| @@ -94,6 +107,10 @@ export async function loadInstructMode(data) { | ||
| 94 | 107 | |
| 95 | 108 | if (control.isCheckbox) { |
| 96 | 109 | $element.prop('checked', power_user.instruct[control.property]); |
| 110 | + } else if ($element.is('select')) { | |
| 111 | + const value = power_user.instruct[control.property]; | |
| 112 | + $element.val(value); | |
| 113 | + $element.filter(`[value="${value}"]`).prop('checked', true); | |
| 97 | 114 | } else { |
| 98 | 115 | $element.val(power_user.instruct[control.property]); |
| 99 | 116 | } |
| @@ -119,12 +136,6 @@ export async function loadInstructMode(data) { | ||
| 119 | 136 | option.selected = name === power_user.instruct.preset; |
| 120 | 137 | $('#instruct_presets').append(option); |
| 121 | 138 | }); |
| 122 | - | |
| 123 | - highlightDefaultPreset(); | |
| 124 | -} | |
| 125 | - | |
| 126 | -function highlightDefaultPreset() { | |
| 127 | - $('#instruct_set_default').toggleClass('default', power_user.default_instruct === power_user.instruct.preset); | |
| 128 | 139 | } |
| 129 | 140 | |
| 130 | 141 | /** |
| @@ -141,13 +152,6 @@ export function selectContextPreset(preset, { quiet = false, isAuto = false } = | ||
| 141 | 152 | !quiet && toastr.info(`Context Template: "${preset}" ${isAuto ? 'auto-' : ''}selected`); |
| 142 | 153 | } |
| 143 | 154 | |
| 144 | - // If instruct mode is disabled, enable it, except for default context template | |
| 145 | - if (!power_user.instruct.enabled && preset !== power_user.default_context) { | |
| 146 | - power_user.instruct.enabled = true; | |
| 147 | - $('#instruct_enabled').prop('checked', true).trigger('change'); | |
| 148 | - !quiet && toastr.info('Instruct Mode enabled'); | |
| 149 | - } | |
| 150 | - | |
| 151 | 155 | saveSettingsDebounced(); |
| 152 | 156 | } |
| 153 | 157 | |
| @@ -217,13 +221,6 @@ export function autoSelectInstructPreset(modelId) { | ||
| 217 | 221 | } |
| 218 | 222 | } |
| 219 | 223 | } |
| 220 | - | |
| 221 | - if (power_user.instruct.bind_to_context && power_user.default_instruct && power_user.instruct.preset !== power_user.default_instruct) { | |
| 222 | - if (instruct_presets.some(p => p.name === power_user.default_instruct)) { | |
| 223 | - console.log(`Instruct mode: default preset "${power_user.default_instruct}" selected`); | |
| 224 | - $('#instruct_presets').val(power_user.default_instruct).trigger('change'); | |
| 225 | - } | |
| 226 | - } | |
| 227 | 224 | } |
| 228 | 225 | |
| 229 | 226 | return false; |
| @@ -311,9 +308,9 @@ export const force_output_sequence = { | ||
| 311 | 308 | * @returns {string} Formatted instruct mode chat message. |
| 312 | 309 | */ |
| 313 | 310 | export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2, forceOutputSequence) { |
| 314 | 311 | let includeNames = isNarrator ? false : power_user.instruct.namesnames_behavior === names_behavior_types.ALWAYS; |
| 315 | 312 | |
| 316 | 313 | if (!isNarrator && power_user.instruct.names_force_groupsnames_behavior === names_behavior_types.FORCE && ((selected_group && name !== name1) || (forceAvatar && name !== name1))) { |
| 317 | 314 | includeNames = true; |
| 318 | 315 | } |
| 319 | 316 | |
| @@ -416,7 +413,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) { | ||
| 416 | 413 | return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading)); |
| 417 | 414 | } |
| 418 | 415 | |
| 419 | 416 | const includeNames = power_user.instruct.namesnames_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_force_groupsnames_behavior === names_behavior_types.FORCE); |
| 420 | 417 | |
| 421 | 418 | let inputPrefix = power_user.instruct.input_sequence || ''; |
| 422 | 419 | let outputPrefix = power_user.instruct.output_sequence || ''; |
| @@ -460,7 +457,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) { | ||
| 460 | 457 | |
| 461 | 458 | for (const example of blockExamples) { |
| 462 | 459 | // If force group/persona names is set, we should override the include names for the user placeholder |
| 463 | 460 | const includeThisName = includeNames || (power_user.instruct.names_force_groupsnames_behavior === names_behavior_types.FORCE && example.name == 'example_user'); |
| 464 | 461 | |
| 465 | 462 | const prefix = example.name == 'example_user' ? inputPrefix : outputPrefix; |
| 466 | 463 | const suffix = example.name == 'example_user' ? inputSuffix : outputSuffix; |
| @@ -490,7 +487,7 @@ export function formatInstructModeExamples(mesExamplesArray, name1, name2) { | ||
| 490 | 487 | * @returns {string} Formatted instruct mode last prompt line. |
| 491 | 488 | */ |
| 492 | 489 | export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2, isQuiet, isQuietToLoud) { |
| 493 | 490 | const includeNames = name && (power_user.instruct.namesnames_behavior === names_behavior_types.ALWAYS || (!!selected_group && power_user.instruct.names_force_groupsnames_behavior === names_behavior_types.FORCE)) && !(isQuiet && !isQuietToLoud); |
| 494 | 491 | |
| 495 | 492 | function getSequence() { |
| 496 | 493 | // User impersonation prompt |
| @@ -514,6 +511,19 @@ export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, | ||
| 514 | 511 | } |
| 515 | 512 | |
| 516 | 513 | let sequence = getSequence() || ''; |
| 514 | + let nameFiller = ''; | |
| 515 | + | |
| 516 | + // A hack for Mistral's formatting that has a normal output sequence ending with a space | |
| 517 | + if ( | |
| 518 | + includeNames && | |
| 519 | + power_user.instruct.last_output_sequence && | |
| 520 | + power_user.instruct.output_sequence && | |
| 521 | + sequence === power_user.instruct.last_output_sequence && | |
| 522 | + /\s$/.test(power_user.instruct.output_sequence) && | |
| 523 | + !/\s$/.test(power_user.instruct.last_output_sequence) | |
| 524 | + ) { | |
| 525 | + nameFiller = power_user.instruct.output_sequence.slice(-1); | |
| 526 | + } | |
| 517 | 527 | |
| 518 | 528 | if (power_user.instruct.macro) { |
| 519 | 529 | sequence = substituteParams(sequence, name1, name2); |
| @@ -521,7 +531,7 @@ export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, | ||
| 521 | 531 | } |
| 522 | 532 | |
| 523 | 533 | const separator = power_user.instruct.wrap ? '\n' : ''; |
| 524 | 534 | let text = includeNames ? (separator + sequence + separator + nameFiller + `${name}:`) : (separator + sequence); |
| 525 | 535 | |
| 526 | 536 | // Quiet prompt already has a newline at the end |
| 527 | 537 | if (isQuiet && separator) { |
| @@ -549,10 +559,6 @@ function selectMatchingContextTemplate(name) { | ||
| 549 | 559 | break; |
| 550 | 560 | } |
| 551 | 561 | } |
| 552 | - if (!foundMatch) { | |
| 553 | - // If no match was found, select default context preset | |
| 554 | - selectContextPreset(power_user.default_context, { isAuto: true }); | |
| 555 | - } | |
| 556 | 562 | } |
| 557 | 563 | |
| 558 | 564 | /** |
| @@ -566,9 +572,13 @@ export function replaceInstructMacros(input, env) { | ||
| 566 | 572 | if (!input) { |
| 567 | 573 | return ''; |
| 568 | 574 | } |
| 575 | + | |
| 576 | + const syspromptMacros = { | |
| 577 | + 'systemPrompt': (power_user.prefer_character_prompt && env.charPrompt ? env.charPrompt : power_user.sysprompt.content), | |
| 578 | + 'defaultSystemPrompt|instructSystem|instructSystemPrompt': power_user.sysprompt.content, | |
| 579 | + }; | |
| 580 | + | |
| 569 | 581 | const instructMacros = { |
| 570 | - 'systemPrompt': (power_user.prefer_character_prompt && env.charPrompt ? env.charPrompt : power_user.instruct.system_prompt), | |
| 571 | - 'instructSystem|instructSystemPrompt': power_user.instruct.system_prompt, | |
| 572 | 582 | 'instructSystemPromptPrefix': power_user.instruct.system_sequence_prefix, |
| 573 | 583 | 'instructSystemPromptSuffix': power_user.instruct.system_sequence_suffix, |
| 574 | 584 | 'instructInput|instructUserPrefix': power_user.instruct.input_sequence, |
| @@ -591,6 +601,11 @@ export function replaceInstructMacros(input, env) { | ||
| 591 | 601 | input = input.replace(regex, power_user.instruct.enabled ? value : ''); |
| 592 | 602 | } |
| 593 | 603 | |
| 604 | + for (const [placeholder, value] of Object.entries(syspromptMacros)) { | |
| 605 | + const regex = new RegExp(`{{(${placeholder})}}`, 'gi'); | |
| 606 | + input = input.replace(regex, power_user.sysprompt.enabled ? value : ''); | |
| 607 | + } | |
| 608 | + | |
| 594 | 609 | input = input.replace(/{{exampleSeparator}}/gi, power_user.context.example_separator); |
| 595 | 610 | input = input.replace(/{{chatStart}}/gi, power_user.context.chat_start); |
| 596 | 611 | |
| @@ -598,20 +613,6 @@ export function replaceInstructMacros(input, env) { | ||
| 598 | 613 | } |
| 599 | 614 | |
| 600 | 615 | jQuery(() => { |
| 601 | - $('#instruct_set_default').on('click', function () { | |
| 602 | - if (power_user.instruct.preset === power_user.default_instruct) { | |
| 603 | - power_user.default_instruct = null; | |
| 604 | - $(this).removeClass('default'); | |
| 605 | - toastr.info('Default instruct template cleared'); | |
| 606 | - } else { | |
| 607 | - power_user.default_instruct = power_user.instruct.preset; | |
| 608 | - $(this).addClass('default'); | |
| 609 | - toastr.info(`Default instruct template set to ${power_user.default_instruct}`); | |
| 610 | - } | |
| 611 | - | |
| 612 | - saveSettingsDebounced(); | |
| 613 | - }); | |
| 614 | - | |
| 615 | 616 | $('#instruct_system_same_as_user').on('input', function () { |
| 616 | 617 | const state = !!$(this).prop('checked'); |
| 617 | 618 | if (state) { |
| @@ -640,9 +641,6 @@ jQuery(() => { | ||
| 640 | 641 | // When instruct mode gets enabled, select context template matching selected instruct preset |
| 641 | 642 | if (power_user.instruct.enabled) { |
| 642 | 643 | selectMatchingContextTemplate(power_user.instruct.preset); |
| 643 | - } else { | |
| 644 | - // When instruct mode gets disabled, select default context preset | |
| 645 | - selectContextPreset(power_user.default_context); | |
| 646 | 644 | } |
| 647 | 645 | }); |
| 648 | 646 | |
| @@ -668,6 +666,10 @@ jQuery(() => { | ||
| 668 | 666 | |
| 669 | 667 | if (control.isCheckbox) { |
| 670 | 668 | $element.prop('checked', power_user.instruct[control.property]).trigger('input'); |
| 669 | + } else if ($element.is('select')) { | |
| 670 | + const value = power_user.instruct[control.property]; | |
| 671 | + $element.val(value); | |
| 672 | + $element.filter(`[value="${value}"]`).prop('checked', true).trigger('input'); | |
| 671 | 673 | } else { |
| 672 | 674 | $element.val(power_user.instruct[control.property]); |
| 673 | 675 | $element.trigger('input'); |
| @@ -679,7 +681,13 @@ jQuery(() => { | ||
| 679 | 681 | // Select matching context template |
| 680 | 682 | selectMatchingContextTemplate(name); |
| 681 | 683 | } |
| 684 | + }); | |
| 682 | 685 | |
| 683 | - highlightDefaultPreset(); | |
| 686 | + if (!CSS.supports('field-sizing', 'content')) { | |
| 687 | + $('#InstructSequencesColumn details').on('toggle', function () { | |
| 688 | + if ($(this).prop('open')) { | |
| 689 | + resetScrollHeight($(this).find('textarea')); | |
| 690 | + } | |
| 684 | 691 | }); |
| 692 | + } | |
| 685 | 693 | }); |
| @@ -5,9 +5,7 @@ import { | ||
| 5 | 5 | novelai_setting_names, |
| 6 | 6 | saveSettingsDebounced, |
| 7 | 7 | setGenerationParamsFromPreset, |
| 8 | - substituteParams, | |
| 9 | 8 | } from '../script.js'; |
| 10 | -import { getCfgPrompt } from './cfg-scale.js'; | |
| 11 | 9 | import { MAX_CONTEXT_DEFAULT, MAX_RESPONSE_DEFAULT, power_user } from './power-user.js'; |
| 12 | 10 | import { getTextTokens, tokenizers } from './tokenizers.js'; |
| 13 | 11 | import { getEventSourceStream } from './sse-stream.js'; |
| @@ -24,6 +22,7 @@ const maximum_output_length = 150; | ||
| 24 | 22 | const default_presets = { |
| 25 | 23 | 'clio-v1': 'Talker-Chat-Clio', |
| 26 | 24 | 'kayra-v1': 'Carefree-Kayra', |
| 25 | + 'llama-3-erato-v1': 'Erato-Dragonfruit', | |
| 27 | 26 | }; |
| 28 | 27 | |
| 29 | 28 | export const nai_settings = { |
| @@ -38,13 +37,16 @@ export const nai_settings = { | ||
| 38 | 37 | top_p: 0.75, |
| 39 | 38 | top_a: 0.08, |
| 40 | 39 | typical_p: 0.975, |
| 40 | + min_p: 0, | |
| 41 | + math1_temp: 1, | |
| 42 | + math1_quad: 0, | |
| 43 | + math1_quad_entropy_scale: 0, | |
| 41 | 44 | min_length: 1, |
| 42 | 45 | model_novel: 'clio-v1', |
| 43 | 46 | preset_settings_novel: 'Talker-Chat-Clio', |
| 44 | 47 | streaming_novel: false, |
| 45 | 48 | preamble: default_preamble, |
| 46 | 49 | prefix: '', |
| 47 | - cfg_uc: '', | |
| 48 | 50 | banned_tokens: '', |
| 49 | 51 | order: default_order, |
| 50 | 52 | logit_bias: [], |
| @@ -57,6 +59,19 @@ const nai_tiers = { | ||
| 57 | 59 | 3: 'Opus', |
| 58 | 60 | }; |
| 59 | 61 | |
| 62 | +const samplers = { | |
| 63 | + temperature: 0, | |
| 64 | + top_k: 1, | |
| 65 | + top_p: 2, | |
| 66 | + tfs: 3, | |
| 67 | + top_a: 4, | |
| 68 | + typical_p: 5, | |
| 69 | + // removed samplers were here | |
| 70 | + mirostat: 8, | |
| 71 | + math1: 9, | |
| 72 | + min_p: 10, | |
| 73 | +}; | |
| 74 | + | |
| 60 | 75 | let novel_data = null; |
| 61 | 76 | let badWordsCache = {}; |
| 62 | 77 | const BIAS_KEY = '#novel_api-settings'; |
| @@ -91,6 +106,37 @@ export function getKayraMaxResponseTokens() { | ||
| 91 | 106 | return maximum_output_length; |
| 92 | 107 | } |
| 93 | 108 | |
| 109 | +export function convertNovelPreset(data) { | |
| 110 | + if (!data || typeof data !== 'object' || data.presetVersion !== 3 || !data.parameters || typeof data.parameters !== 'object') { | |
| 111 | + return data; | |
| 112 | + } | |
| 113 | + | |
| 114 | + return { | |
| 115 | + max_context: 8000, | |
| 116 | + temperature: data.parameters.temperature, | |
| 117 | + max_length: data.parameters.max_length, | |
| 118 | + min_length: data.parameters.min_length, | |
| 119 | + top_k: data.parameters.top_k, | |
| 120 | + top_p: data.parameters.top_p, | |
| 121 | + top_a: data.parameters.top_a, | |
| 122 | + typical_p: data.parameters.typical_p, | |
| 123 | + tail_free_sampling: data.parameters.tail_free_sampling, | |
| 124 | + repetition_penalty: data.parameters.repetition_penalty, | |
| 125 | + repetition_penalty_range: data.parameters.repetition_penalty_range, | |
| 126 | + repetition_penalty_slope: data.parameters.repetition_penalty_slope, | |
| 127 | + repetition_penalty_frequency: data.parameters.repetition_penalty_frequency, | |
| 128 | + repetition_penalty_presence: data.parameters.repetition_penalty_presence, | |
| 129 | + phrase_rep_pen: data.parameters.phrase_rep_pen, | |
| 130 | + mirostat_lr: data.parameters.mirostat_lr, | |
| 131 | + mirostat_tau: data.parameters.mirostat_tau, | |
| 132 | + math1_temp: data.parameters.math1_temp, | |
| 133 | + math1_quad: data.parameters.math1_quad, | |
| 134 | + math1_quad_entropy_scale: data.parameters.math1_quad_entropy_scale, | |
| 135 | + min_p: data.parameters.min_p, | |
| 136 | + order: Array.isArray(data.parameters.order) ? data.parameters.order.filter(s => s.enabled && Object.keys(samplers).includes(s.id)).map(s => samplers[s.id]) : default_order, | |
| 137 | + }; | |
| 138 | +} | |
| 139 | + | |
| 94 | 140 | export function getNovelTier() { |
| 95 | 141 | return nai_tiers[novel_data?.tier] ?? 'no_connection'; |
| 96 | 142 | } |
| @@ -141,16 +187,18 @@ export function loadNovelPreset(preset) { | ||
| 141 | 187 | nai_settings.top_a = preset.top_a; |
| 142 | 188 | nai_settings.typical_p = preset.typical_p; |
| 143 | 189 | nai_settings.min_length = preset.min_length; |
| 144 | - nai_settings.cfg_scale = preset.cfg_scale; | |
| 145 | 190 | nai_settings.phrase_rep_pen = preset.phrase_rep_pen; |
| 146 | 191 | nai_settings.mirostat_lr = preset.mirostat_lr; |
| 147 | 192 | nai_settings.mirostat_tau = preset.mirostat_tau; |
| 148 | 193 | nai_settings.prefix = preset.prefix; |
| 149 | - nai_settings.cfg_uc = preset.cfg_uc || ''; | |
| 150 | 194 | nai_settings.banned_tokens = preset.banned_tokens || ''; |
| 151 | 195 | nai_settings.order = preset.order || default_order; |
| 152 | 196 | nai_settings.logit_bias = preset.logit_bias || []; |
| 153 | 197 | nai_settings.preamble = preset.preamble || default_preamble; |
| 198 | + nai_settings.min_p = preset.min_p || 0; | |
| 199 | + nai_settings.math1_temp = preset.math1_temp || 1; | |
| 200 | + nai_settings.math1_quad = preset.math1_quad || 0; | |
| 201 | + nai_settings.math1_quad_entropy_scale = preset.math1_quad_entropy_scale || 0; | |
| 154 | 202 | loadNovelSettingsUi(nai_settings); |
| 155 | 203 | } |
| 156 | 204 | |
| @@ -178,16 +226,18 @@ export function loadNovelSettings(settings) { | ||
| 178 | 226 | nai_settings.typical_p = settings.typical_p; |
| 179 | 227 | nai_settings.min_length = settings.min_length; |
| 180 | 228 | nai_settings.phrase_rep_pen = settings.phrase_rep_pen; |
| 181 | - nai_settings.cfg_scale = settings.cfg_scale; | |
| 182 | 229 | nai_settings.mirostat_lr = settings.mirostat_lr; |
| 183 | 230 | nai_settings.mirostat_tau = settings.mirostat_tau; |
| 184 | 231 | nai_settings.streaming_novel = !!settings.streaming_novel; |
| 185 | 232 | nai_settings.preamble = settings.preamble || default_preamble; |
| 186 | 233 | nai_settings.prefix = settings.prefix; |
| 187 | - nai_settings.cfg_uc = settings.cfg_uc || ''; | |
| 188 | 234 | nai_settings.banned_tokens = settings.banned_tokens || ''; |
| 189 | 235 | nai_settings.order = settings.order || default_order; |
| 190 | 236 | nai_settings.logit_bias = settings.logit_bias || []; |
| 237 | + nai_settings.min_p = settings.min_p || 0; | |
| 238 | + nai_settings.math1_temp = settings.math1_temp || 1; | |
| 239 | + nai_settings.math1_quad = settings.math1_quad || 0; | |
| 240 | + nai_settings.math1_quad_entropy_scale = settings.math1_quad_entropy_scale || 0; | |
| 191 | 241 | loadNovelSettingsUi(nai_settings); |
| 192 | 242 | } |
| 193 | 243 | |
| @@ -214,8 +264,6 @@ function loadNovelSettingsUi(ui_settings) { | ||
| 214 | 264 | $('#top_a_counter_novel').val(Number(ui_settings.top_a).toFixed(3)); |
| 215 | 265 | $('#typical_p_novel').val(ui_settings.typical_p); |
| 216 | 266 | $('#typical_p_counter_novel').val(Number(ui_settings.typical_p).toFixed(3)); |
| 217 | - $('#cfg_scale_novel').val(ui_settings.cfg_scale); | |
| 218 | - $('#cfg_scale_counter_novel').val(Number(ui_settings.cfg_scale).toFixed(2)); | |
| 219 | 267 | $('#phrase_rep_pen_novel').val(ui_settings.phrase_rep_pen || 'off'); |
| 220 | 268 | $('#mirostat_lr_novel').val(ui_settings.mirostat_lr); |
| 221 | 269 | $('#mirostat_lr_counter_novel').val(Number(ui_settings.mirostat_lr).toFixed(2)); |
| @@ -225,8 +273,15 @@ function loadNovelSettingsUi(ui_settings) { | ||
| 225 | 273 | $('#min_length_counter_novel').val(Number(ui_settings.min_length).toFixed(0)); |
| 226 | 274 | $('#nai_preamble_textarea').val(ui_settings.preamble); |
| 227 | 275 | $('#nai_prefix').val(ui_settings.prefix || 'vanilla'); |
| 228 | - $('#nai_cfg_uc').val(ui_settings.cfg_uc || ''); | |
| 229 | 276 | $('#nai_banned_tokens').val(ui_settings.banned_tokens || ''); |
| 277 | + $('#min_p_novel').val(ui_settings.min_p); | |
| 278 | + $('#min_p_counter_novel').val(Number(ui_settings.min_p).toFixed(3)); | |
| 279 | + $('#math1_temp_novel').val(ui_settings.math1_temp); | |
| 280 | + $('#math1_temp_counter_novel').val(Number(ui_settings.math1_temp).toFixed(2)); | |
| 281 | + $('#math1_quad_novel').val(ui_settings.math1_quad); | |
| 282 | + $('#math1_quad_counter_novel').val(Number(ui_settings.math1_quad).toFixed(2)); | |
| 283 | + $('#math1_quad_entropy_scale_novel').val(ui_settings.math1_quad_entropy_scale); | |
| 284 | + $('#math1_quad_entropy_scale_counter_novel').val(Number(ui_settings.math1_quad_entropy_scale).toFixed(2)); | |
| 230 | 285 | |
| 231 | 286 | $('#streaming_novel').prop('checked', ui_settings.streaming_novel); |
| 232 | 287 | sortItemsByOrder(ui_settings.order); |
| @@ -313,29 +368,41 @@ const sliders = [ | ||
| 313 | 368 | setValue: (val) => { nai_settings.mirostat_lr = Number(val).toFixed(2); }, |
| 314 | 369 | }, |
| 315 | 370 | { |
| 316 | - sliderId: '#cfg_scale_novel', | |
| 317 | - counterId: '#cfg_scale_counter_novel', | |
| 318 | - format: (val) => `${val}`, | |
| 319 | - setValue: (val) => { nai_settings.cfg_scale = Number(val).toFixed(2); }, | |
| 320 | - }, | |
| 321 | - { | |
| 322 | 371 | sliderId: '#min_length_novel', |
| 323 | 372 | counterId: '#min_length_counter_novel', |
| 324 | 373 | format: (val) => `${val}`, |
| 325 | 374 | setValue: (val) => { nai_settings.min_length = Number(val).toFixed(0); }, |
| 326 | 375 | }, |
| 327 | 376 | { |
| 328 | - sliderId: '#nai_cfg_uc', | |
| 329 | - counterId: '#nai_cfg_uc_counter', | |
| 330 | - format: (val) => val, | |
| 331 | - setValue: (val) => { nai_settings.cfg_uc = val; }, | |
| 332 | - }, | |
| 333 | - { | |
| 334 | 377 | sliderId: '#nai_banned_tokens', |
| 335 | 378 | counterId: '#nai_banned_tokens_counter', |
| 336 | 379 | format: (val) => val, |
| 337 | 380 | setValue: (val) => { nai_settings.banned_tokens = val; }, |
| 338 | 381 | }, |
| 382 | + { | |
| 383 | + sliderId: '#min_p_novel', | |
| 384 | + counterId: '#min_p_counter_novel', | |
| 385 | + format: (val) => Number(val).toFixed(3), | |
| 386 | + setValue: (val) => { nai_settings.min_p = Number(val); }, | |
| 387 | + }, | |
| 388 | + { | |
| 389 | + sliderId: '#math1_temp_novel', | |
| 390 | + counterId: '#math1_temp_counter_novel', | |
| 391 | + format: (val) => Number(val).toFixed(2), | |
| 392 | + setValue: (val) => { nai_settings.math1_temp = Number(val); }, | |
| 393 | + }, | |
| 394 | + { | |
| 395 | + sliderId: '#math1_quad_novel', | |
| 396 | + counterId: '#math1_quad_counter_novel', | |
| 397 | + format: (val) => Number(val).toFixed(2), | |
| 398 | + setValue: (val) => { nai_settings.math1_quad = Number(val); }, | |
| 399 | + }, | |
| 400 | + { | |
| 401 | + sliderId: '#math1_quad_entropy_scale_novel', | |
| 402 | + counterId: '#math1_quad_entropy_scale_counter_novel', | |
| 403 | + format: (val) => Number(val).toFixed(2), | |
| 404 | + setValue: (val) => { nai_settings.math1_quad_entropy_scale = Number(val); }, | |
| 405 | + }, | |
| 339 | 406 | ]; |
| 340 | 407 | |
| 341 | 408 | function getBadWordIds(banned_tokens, tokenizerType) { |
| @@ -423,11 +490,8 @@ function getBadWordPermutations(text) { | ||
| 423 | 490 | return result.filter(onlyUnique); |
| 424 | 491 | } |
| 425 | 492 | |
| 426 | 493 | export function getNovelGenerationData(finalPrompt, settings, maxLength, isImpersonate, isContinue, cfgValues_cfgValues, type) { |
| 427 | 494 | console.debug('NovelAI generation data for', type); |
| 428 | - if (cfgValues && cfgValues.guidanceScale && cfgValues.guidanceScale?.value !== 1) { | |
| 429 | - cfgValues.negativePrompt = (getCfgPrompt(cfgValues.guidanceScale, true))?.value; | |
| 430 | - } | |
| 431 | 495 | |
| 432 | 496 | const tokenizerType = getTokenizerTypeForModel(nai_settings.model_novel); |
| 433 | 497 | const stopSequences = (tokenizerType !== tokenizers.NONE) |
| @@ -451,7 +515,14 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper | ||
| 451 | 515 | console.log(finalPrompt); |
| 452 | 516 | } |
| 453 | 517 | |
| 454 | 518 | const adjustedMaxLengthisKayra = nai_settings.model_novel.includes('kayra') ? getKayraMaxResponseTokens() : maximum_output_length; |
| 519 | + const isErato = nai_settings.model_novel.includes('erato'); | |
| 520 | + | |
| 521 | + if (isErato) { | |
| 522 | + finalPrompt = '<|startoftext|>' + finalPrompt; | |
| 523 | + } | |
| 524 | + | |
| 525 | + const adjustedMaxLength = (isKayra || isErato) ? getKayraMaxResponseTokens() : maximum_output_length; | |
| 455 | 526 | |
| 456 | 527 | return { |
| 457 | 528 | 'input': finalPrompt, |
| @@ -469,11 +540,13 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper | ||
| 469 | 540 | 'top_a': Number(nai_settings.top_a), |
| 470 | 541 | 'top_p': Number(nai_settings.top_p), |
| 471 | 542 | 'top_k': Number(nai_settings.top_k), |
| 543 | + 'min_p': Number(nai_settings.min_p), | |
| 544 | + 'math1_temp': Number(nai_settings.math1_temp), | |
| 545 | + 'math1_quad': Number(nai_settings.math1_quad), | |
| 546 | + 'math1_quad_entropy_scale': Number(nai_settings.math1_quad_entropy_scale), | |
| 472 | 547 | 'typical_p': Number(nai_settings.typical_p), |
| 473 | 548 | 'mirostat_lr': Number(nai_settings.mirostat_lr), |
| 474 | 549 | 'mirostat_tau': Number(nai_settings.mirostat_tau), |
| 475 | - 'cfg_scale': cfgValues?.guidanceScale?.value ?? Number(nai_settings.cfg_scale), | |
| 476 | - 'cfg_uc': cfgValues?.negativePrompt ?? substituteParams(nai_settings.cfg_uc) ?? '', | |
| 477 | 550 | 'phrase_rep_pen': nai_settings.phrase_rep_pen, |
| 478 | 551 | 'stop_sequences': stopSequences, |
| 479 | 552 | 'bad_words_ids': badWordIds, |
| @@ -492,7 +565,8 @@ function selectPrefix(selected_prefix, finalPrompt) { | ||
| 492 | 565 | let useInstruct = false; |
| 493 | 566 | const clio = nai_settings.model_novel.includes('clio'); |
| 494 | 567 | const kayra = nai_settings.model_novel.includes('kayra'); |
| 495 | - const isNewModel = clio || kayra; | |
| 568 | + const erato = nai_settings.model_novel.includes('erato'); | |
| 569 | + const isNewModel = clio || kayra || erato; | |
| 496 | 570 | |
| 497 | 571 | if (isNewModel) { |
| 498 | 572 | // NovelAI claims they scan backwards 1000 characters (not tokens!) to look for instruct brackets. That's really short. |
| @@ -511,6 +585,9 @@ function getTokenizerTypeForModel(model) { | ||
| 511 | 585 | if (model.includes('kayra')) { |
| 512 | 586 | return tokenizers.NERD2; |
| 513 | 587 | } |
| 588 | + if (model.includes('erato')) { | |
| 589 | + return tokenizers.LLAMA3; | |
| 590 | + } | |
| 514 | 591 | return tokenizers.NONE; |
| 515 | 592 | } |
| 516 | 593 | |
| @@ -431,7 +431,7 @@ function setOpenAIMessages(chat) { | ||
| 431 | 431 | case character_names_behavior.NONE: |
| 432 | 432 | break; |
| 433 | 433 | case character_names_behavior.DEFAULT: |
| 434 | 434 | if ((selected_group && chat[j].name !== name1) || (chat[j].force_avatar && chat[j].name !== name1 && chat[j].extra?.type !== system_message_types.NARRATOR)) { |
| 435 | 435 | content = `${chat[j].name}: ${content}`; |
| 436 | 436 | } |
| 437 | 437 | break; |
| @@ -855,6 +855,12 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | ||
| 855 | 855 | } |
| 856 | 856 | |
| 857 | 857 | const prompt = prompts.get(source); |
| 858 | + | |
| 859 | + if (prompt.injection_position === INJECTION_POSITION.ABSOLUTE) { | |
| 860 | + promptManager.log(`Skipping prompt ${source} because it is an absolute prompt`); | |
| 861 | + return; | |
| 862 | + } | |
| 863 | + | |
| 858 | 864 | const index = target ? prompts.index(target) : prompts.index(source); |
| 859 | 865 | const collection = new MessageCollection(source); |
| 860 | 866 | collection.add(Message.fromPrompt(prompt)); |
| @@ -899,8 +905,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | ||
| 899 | 905 | acc.push(prompt.identifier); |
| 900 | 906 | return acc; |
| 901 | 907 | }, []); |
| 902 | 908 | const userAbsolutePromptsabsolutePrompts = prompts.collection |
| 903 | 909 | .filter((prompt) => false === prompt.system_prompt && prompt.injection_position === INJECTION_POSITION.ABSOLUTE) |
| 904 | 910 | .reduce((acc, prompt) => { |
| 905 | 911 | acc.push(prompt); |
| 906 | 912 | return acc; |
| @@ -965,7 +971,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | ||
| 965 | 971 | } |
| 966 | 972 | |
| 967 | 973 | // Add in-chat injections |
| 968 | 974 | messages = populationInjectionPrompts(userAbsolutePromptsabsolutePrompts, messages); |
| 969 | 975 | |
| 970 | 976 | // Decide whether dialogue examples should always be added |
| 971 | 977 | if (power_user.pin_examples) { |
| @@ -1102,6 +1108,18 @@ function preparePromptsForChatCompletion({ Scenario, charPersonality, name2, wor | ||
| 1102 | 1108 | |
| 1103 | 1109 | // Merge system prompts with prompt manager prompts |
| 1104 | 1110 | systemPrompts.forEach(prompt => { |
| 1111 | + const collectionPrompt = prompts.get(prompt.identifier); | |
| 1112 | + | |
| 1113 | + // Apply system prompt role/depth overrides if they set in the prompt manager | |
| 1114 | + if (collectionPrompt) { | |
| 1115 | + // In-Chat / Relative | |
| 1116 | + prompt.injection_position = collectionPrompt.injection_position ?? prompt.injection_position; | |
| 1117 | + // Depth for In-Chat | |
| 1118 | + prompt.injection_depth = collectionPrompt.injection_depth ?? prompt.injection_depth; | |
| 1119 | + // Role (system, user, assistant) | |
| 1120 | + prompt.role = collectionPrompt.role ?? prompt.role; | |
| 1121 | + } | |
| 1122 | + | |
| 1105 | 1123 | const newPrompt = promptManager.preparePrompt(prompt); |
| 1106 | 1124 | const markerIndex = prompts.index(prompt.identifier); |
| 1107 | 1125 | |
| @@ -2370,7 +2388,7 @@ class Message { | ||
| 2370 | 2388 | * @returns {Promise<string>} Compressed image as a Data URL. |
| 2371 | 2389 | */ |
| 2372 | 2390 | async compressImage(image) { |
| 2373 | 2391 | if ([chat_completion_sources.OPENROUTER, chat_completion_sources.MAKERSUITE, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) { |
| 2374 | 2392 | const sizeThreshold = 2 * 1024 * 1024; |
| 2375 | 2393 | const dataSize = image.length * 0.75; |
| 2376 | 2394 | const maxSide = 1024; |
| @@ -2747,7 +2765,7 @@ export class ChatCompletion { | ||
| 2747 | 2765 | const message = { role: item.role, content: item.content, ...(item.name ? { name: item.name } : {}) }; |
| 2748 | 2766 | chat.push(message); |
| 2749 | 2767 | } else { |
| 2750 | 2768 | consolethis.warnlog('Invalid`Skipping invalid or empty message in collection',: ${JSON.stringify(item)}`); |
| 2751 | 2769 | } |
| 2752 | 2770 | } |
| 2753 | 2771 | return chat; |
| @@ -3216,12 +3234,12 @@ async function getStatusOpen() { | ||
| 3216 | 3234 | |
| 3217 | 3235 | const responseData = await response.json(); |
| 3218 | 3236 | |
| 3219 | - if (!('error' in responseData)) { | |
| 3220 | - setOnlineStatus('Valid'); | |
| 3221 | - } | |
| 3222 | 3237 | if ('data' in responseData && Array.isArray(responseData.data)) { |
| 3223 | 3238 | saveModelList(responseData.data); |
| 3224 | 3239 | } |
| 3240 | + if (!('error' in responseData)) { | |
| 3241 | + setOnlineStatus('Valid'); | |
| 3242 | + } | |
| 3225 | 3243 | } catch (error) { |
| 3226 | 3244 | console.error(error); |
| 3227 | 3245 | |
| @@ -3986,6 +4004,8 @@ async function onModelChange() { | ||
| 3986 | 4004 | $('#openai_max_context').attr('max', max_2mil); |
| 3987 | 4005 | } else if (value.includes('gemini-1.5-pro')) { |
| 3988 | 4006 | $('#openai_max_context').attr('max', max_2mil); |
| 4007 | + } else if (value.match('gemini-1.5-flash-002')) { | |
| 4008 | + $('#openai_max_context').attr('max', max_2mil); | |
| 3989 | 4009 | } else if (value.includes('gemini-1.5-flash')) { |
| 3990 | 4010 | $('#openai_max_context').attr('max', max_1mil); |
| 3991 | 4011 | } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') { |
| @@ -4097,6 +4117,8 @@ async function onModelChange() { | ||
| 4097 | 4117 | $('#openai_max_context').attr('max', max_128k); |
| 4098 | 4118 | } else if (oai_settings.mistralai_model.includes('mixtral-8x22b')) { |
| 4099 | 4119 | $('#openai_max_context').attr('max', max_64k); |
| 4120 | + } else if (oai_settings.mistralai_model.includes('pixtral')) { | |
| 4121 | + $('#openai_max_context').attr('max', max_128k); | |
| 4100 | 4122 | } else { |
| 4101 | 4123 | $('#openai_max_context').attr('max', max_32k); |
| 4102 | 4124 | } |
| @@ -4630,12 +4652,15 @@ export function isImageInliningSupported() { | ||
| 4630 | 4652 | 'gemini-1.5-flash', |
| 4631 | 4653 | 'gemini-1.5-flash-latest', |
| 4632 | 4654 | 'gemini-1.5-flash-001', |
| 4655 | + 'gemini-1.5-flash-002', | |
| 4633 | 4656 | 'gemini-1.5-flash-exp-0827', |
| 4634 | 4657 | 'gemini-1.5-flash-8b-exp-0827', |
| 4658 | + 'gemini-1.5-flash-8b-exp-0924', | |
| 4635 | 4659 | 'gemini-1.0-pro-vision-latest', |
| 4636 | 4660 | 'gemini-1.5-pro', |
| 4637 | 4661 | 'gemini-1.5-pro-latest', |
| 4638 | 4662 | 'gemini-1.5-pro-001', |
| 4663 | + 'gemini-1.5-pro-002', | |
| 4639 | 4664 | 'gemini-1.5-pro-exp-0801', |
| 4640 | 4665 | 'gemini-1.5-pro-exp-0827', |
| 4641 | 4666 | 'gemini-pro-vision', |
| @@ -4646,6 +4671,8 @@ export function isImageInliningSupported() { | ||
| 4646 | 4671 | 'gpt-4o-mini', |
| 4647 | 4672 | 'chatgpt-4o-latest', |
| 4648 | 4673 | 'yi-vision', |
| 4674 | + 'pixtral-latest', | |
| 4675 | + 'pixtral-12b-2409', | |
| 4649 | 4676 | ]; |
| 4650 | 4677 | |
| 4651 | 4678 | switch (oai_settings.chat_completion_source) { |
| @@ -4661,6 +4688,8 @@ export function isImageInliningSupported() { | ||
| 4661 | 4688 | return true; |
| 4662 | 4689 | case chat_completion_sources.ZEROONEAI: |
| 4663 | 4690 | return visionSupportedModels.some(model => oai_settings.zerooneai_model.includes(model)); |
| 4691 | + case chat_completion_sources.MISTRALAI: | |
| 4692 | + return visionSupportedModels.some(model => oai_settings.mistralai_model.includes(model)); | |
| 4664 | 4693 | default: |
| 4665 | 4694 | return false; |
| 4666 | 4695 | } |
| @@ -4789,7 +4818,7 @@ function runProxyCallback(_, value) { | ||
| 4789 | 4818 | return foundName; |
| 4790 | 4819 | } |
| 4791 | 4820 | |
| 4792 | 4821 | export function initOpenaiinitOpenAI() { |
| 4793 | 4822 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 4794 | 4823 | name: 'proxy', |
| 4795 | 4824 | callback: runProxyCallback, |
| @@ -4805,9 +4834,7 @@ export function initOpenai() { | ||
| 4805 | 4834 | ], |
| 4806 | 4835 | helpString: 'Sets a proxy preset by name.', |
| 4807 | 4836 | })); |
| 4808 | -} | |
| 4809 | 4837 | |
| 4810 | -$(document).ready(async function () { | |
| 4811 | 4838 | $('#test_api_button').on('click', testApiConnection); |
| 4812 | 4839 | |
| 4813 | 4840 | $('#scale-alt').on('change', function () { |
| @@ -5266,4 +5293,4 @@ $(document).ready(async function () { | ||
| 5266 | 5293 | $('#openai_proxy_password_show').on('click', onProxyPasswordShowClick); |
| 5267 | 5294 | $('#customize_additional_parameters').on('click', onCustomizeParametersClick); |
| 5268 | 5295 | $('#openai_proxy_preset').on('change', onProxyPresetChange); |
| 5269 | 5296 | }); |
| @@ -2,7 +2,7 @@ import { | ||
| 2 | 2 | characters, |
| 3 | 3 | chat, |
| 4 | 4 | chat_metadata, |
| 5 | 5 | default_avatardefault_user_avatar, |
| 6 | 6 | eventSource, |
| 7 | 7 | event_types, |
| 8 | 8 | getRequestHeaders, |
| @@ -357,7 +357,7 @@ async function createDummyPersona() { | ||
| 357 | 357 | // Date + name (only ASCII) to make it unique |
| 358 | 358 | const avatarId = `${Date.now()}-${personaName.replace(/[^a-zA-Z0-9]/g, '')}.png`; |
| 359 | 359 | initPersona(avatarId, personaName, ''); |
| 360 | 360 | await uploadUserAvatar(default_avatardefault_user_avatar, avatarId); |
| 361 | 361 | } |
| 362 | 362 | |
| 363 | 363 | /** |
| @@ -944,7 +944,7 @@ async function onPersonasRestoreInput(e) { | ||
| 944 | 944 | // If the avatar is missing, upload it |
| 945 | 945 | if (!avatarsList.includes(key)) { |
| 946 | 946 | warnings.push(`Persona image "${key}" (${value}) is missing, uploading default avatar`); |
| 947 | 947 | await uploadUserAvatar(default_avatardefault_user_avatar, key); |
| 948 | 948 | } |
| 949 | 949 | } |
| 950 | 950 | |
| @@ -32,6 +32,7 @@ import { | ||
| 32 | 32 | import { |
| 33 | 33 | instruct_presets, |
| 34 | 34 | loadInstructMode, |
| 35 | + names_behavior_types, | |
| 35 | 36 | selectInstructPreset, |
| 36 | 37 | } from './instruct-mode.js'; |
| 37 | 38 | |
| @@ -49,6 +50,7 @@ import { AUTOCOMPLETE_SELECT_KEY, AUTOCOMPLETE_WIDTH } from './autocomplete/Auto | ||
| 49 | 50 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 50 | 51 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 51 | 52 | import { POPUP_TYPE, callGenericPopup } from './popup.js'; |
| 53 | +import { loadSystemPrompts } from './sysprompt.js'; | |
| 52 | 54 | |
| 53 | 55 | export { |
| 54 | 56 | loadPowerUserSettings, |
| @@ -113,7 +115,6 @@ let power_user = { | ||
| 113 | 115 | pin_examples: false, |
| 114 | 116 | strip_examples: false, |
| 115 | 117 | trim_sentences: false, |
| 116 | - include_newline: false, | |
| 117 | 118 | always_force_name2: false, |
| 118 | 119 | user_prompt_bias: '', |
| 119 | 120 | show_user_prompt_bias: true, |
| @@ -205,11 +206,9 @@ let power_user = { | ||
| 205 | 206 | disable_group_trimming: false, |
| 206 | 207 | single_line: false, |
| 207 | 208 | |
| 208 | - default_instruct: '', | |
| 209 | 209 | instruct: { |
| 210 | 210 | enabled: false, |
| 211 | 211 | preset: 'Alpaca', |
| 212 | - system_prompt: 'Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}\'s next reply in a fictional roleplay chat between {{user}} and {{char}}.\n', | |
| 213 | 212 | input_sequence: '### Instruction:', |
| 214 | 213 | input_suffix: '', |
| 215 | 214 | output_sequence: '### Response:', |
| @@ -224,8 +223,7 @@ let power_user = { | ||
| 224 | 223 | stop_sequence: '', |
| 225 | 224 | wrap: true, |
| 226 | 225 | macro: true, |
| 227 | 226 | namesnames_behavior: falsenames_behavior_types.FORCE, |
| 228 | - names_force_groups: true, | |
| 229 | 227 | activation_regex: '', |
| 230 | 228 | bind_to_context: false, |
| 231 | 229 | user_alignment_message: '', |
| @@ -234,7 +232,6 @@ let power_user = { | ||
| 234 | 232 | separator_sequence: '', |
| 235 | 233 | }, |
| 236 | 234 | |
| 237 | - default_context: 'Default', | |
| 238 | 235 | context: { |
| 239 | 236 | preset: 'Default', |
| 240 | 237 | story_string: defaultStoryString, |
| @@ -245,6 +242,12 @@ let power_user = { | ||
| 245 | 242 | names_as_stop_strings: true, |
| 246 | 243 | }, |
| 247 | 244 | |
| 245 | + sysprompt: { | |
| 246 | + enabled: true, | |
| 247 | + name: 'Neutral - Chat', | |
| 248 | + content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', | |
| 249 | + }, | |
| 250 | + | |
| 248 | 251 | personas: {}, |
| 249 | 252 | default_persona: null, |
| 250 | 253 | persona_descriptions: {}, |
| @@ -317,7 +320,6 @@ const contextControls = [ | ||
| 317 | 320 | // Existing power user settings |
| 318 | 321 | { id: 'always-force-name2-checkbox', property: 'always_force_name2', isCheckbox: true, isGlobalSetting: true, defaultValue: true }, |
| 319 | 322 | { id: 'trim_sentences_checkbox', property: 'trim_sentences', isCheckbox: true, isGlobalSetting: true, defaultValue: false }, |
| 320 | - { id: 'include_newline_checkbox', property: 'include_newline', isCheckbox: true, isGlobalSetting: true, defaultValue: false }, | |
| 321 | 323 | { id: 'single_line', property: 'single_line', isCheckbox: true, isGlobalSetting: true, defaultValue: false }, |
| 322 | 324 | ]; |
| 323 | 325 | |
| @@ -1487,7 +1489,6 @@ async function loadPowerUserSettings(settings, data) { | ||
| 1487 | 1489 | $('#collapse-newlines-checkbox').prop('checked', power_user.collapse_newlines); |
| 1488 | 1490 | $('#always-force-name2-checkbox').prop('checked', power_user.always_force_name2); |
| 1489 | 1491 | $('#trim_sentences_checkbox').prop('checked', power_user.trim_sentences); |
| 1490 | - $('#include_newline_checkbox').prop('checked', power_user.include_newline); | |
| 1491 | 1492 | $('#render_formulas').prop('checked', power_user.render_formulas); |
| 1492 | 1493 | $('#disable_group_trimming').prop('checked', power_user.disable_group_trimming); |
| 1493 | 1494 | $('#markdown_escape_strings').val(power_user.markdown_escape_strings); |
| @@ -1598,6 +1599,7 @@ async function loadPowerUserSettings(settings, data) { | ||
| 1598 | 1599 | reloadMarkdownProcessor(power_user.render_formulas); |
| 1599 | 1600 | await loadInstructMode(data); |
| 1600 | 1601 | await loadContextSettings(); |
| 1602 | + await loadSystemPrompts(data); | |
| 1601 | 1603 | loadMaxContextUnlocked(); |
| 1602 | 1604 | switchWaifuMode(); |
| 1603 | 1605 | switchSpoilerMode(); |
| @@ -1817,29 +1819,8 @@ async function loadContextSettings() { | ||
| 1817 | 1819 | } |
| 1818 | 1820 | } |
| 1819 | 1821 | |
| 1820 | - highlightDefaultContext(); | |
| 1821 | - saveSettingsDebounced(); | |
| 1822 | - }); | |
| 1823 | - | |
| 1824 | - $('#context_set_default').on('click', function () { | |
| 1825 | - if (power_user.context.preset !== power_user.default_context) { | |
| 1826 | - power_user.default_context = power_user.context.preset; | |
| 1827 | - $(this).addClass('default'); | |
| 1828 | - toastr.info(`Default context template set to ${power_user.default_context}`); | |
| 1829 | - | |
| 1830 | - highlightDefaultContext(); | |
| 1831 | - | |
| 1832 | 1822 | saveSettingsDebounced(); |
| 1833 | - } | |
| 1834 | 1823 | }); |
| 1835 | - | |
| 1836 | - highlightDefaultContext(); | |
| 1837 | -} | |
| 1838 | - | |
| 1839 | -function highlightDefaultContext() { | |
| 1840 | - $('#context_set_default').toggleClass('default', power_user.default_context === power_user.context.preset); | |
| 1841 | - $('#context_set_default').toggleClass('disabled', power_user.default_context === power_user.context.preset); | |
| 1842 | - $('#context_delete_preset').toggleClass('disabled', power_user.default_context === power_user.context.preset); | |
| 1843 | 1824 | } |
| 1844 | 1825 | |
| 1845 | 1826 | /** |
| @@ -3069,19 +3050,6 @@ $(document).ready(() => { | ||
| 3069 | 3050 | // if trim sentences is unchecked, include newline must be unchecked |
| 3070 | 3051 | $('#trim_sentences_checkbox').change(function () { |
| 3071 | 3052 | power_user.trim_sentences = !!$(this).prop('checked'); |
| 3072 | - if (!$(this).prop('checked')) { | |
| 3073 | - $('#include_newline_checkbox').prop('checked', false); | |
| 3074 | - power_user.include_newline = false; | |
| 3075 | - } | |
| 3076 | - saveSettingsDebounced(); | |
| 3077 | - }); | |
| 3078 | - | |
| 3079 | - $('#include_newline_checkbox').change(function () { | |
| 3080 | - power_user.include_newline = !!$(this).prop('checked'); | |
| 3081 | - if ($(this).prop('checked')) { | |
| 3082 | - $('#trim_sentences_checkbox').prop('checked', true); | |
| 3083 | - power_user.trim_sentences = true; | |
| 3084 | - } | |
| 3085 | 3053 | saveSettingsDebounced(); |
| 3086 | 3054 | }); |
| 3087 | 3055 | |
| @@ -18,13 +18,16 @@ import { | ||
| 18 | 18 | import { groups, selected_group } from './group-chats.js'; |
| 19 | 19 | import { instruct_presets } from './instruct-mode.js'; |
| 20 | 20 | import { kai_settings } from './kai-settings.js'; |
| 21 | 21 | import { PopupconvertNovelPreset } from './popupnai-settings.js'; |
| 22 | +import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; | |
| 22 | 23 | import { context_presets, getContextSettings, power_user } from './power-user.js'; |
| 23 | 24 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 24 | 25 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| 25 | 26 | import { enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 26 | 27 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 27 | 28 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 29 | +import { checkForSystemPromptInInstructTemplate, system_prompts } from './sysprompt.js'; | |
| 30 | +import { renderTemplateAsync } from './templates.js'; | |
| 28 | 31 | import { |
| 29 | 32 | textgenerationwebui_preset_names, |
| 30 | 33 | textgenerationwebui_presets, |
| @@ -71,7 +74,7 @@ function autoSelectPreset() { | ||
| 71 | 74 | * @param {string} apiId API id |
| 72 | 75 | * @returns {PresetManager} Preset manager |
| 73 | 76 | */ |
| 74 | 77 | export function getPresetManager(apiId = '') { |
| 75 | 78 | if (!apiId) { |
| 76 | 79 | apiId = main_api == 'koboldhorde' ? 'kobold' : main_api; |
| 77 | 80 | } |
| @@ -102,6 +105,215 @@ class PresetManager { | ||
| 102 | 105 | this.apiId = apiId; |
| 103 | 106 | } |
| 104 | 107 | |
| 108 | + static masterSections = { | |
| 109 | + 'instruct': { | |
| 110 | + name: 'Instruct Template', | |
| 111 | + getData: () => { | |
| 112 | + const manager = getPresetManager('instruct'); | |
| 113 | + const name = manager.getSelectedPresetName(); | |
| 114 | + return manager.getPresetSettings(name); | |
| 115 | + }, | |
| 116 | + setData: (data) => { | |
| 117 | + const manager = getPresetManager('instruct'); | |
| 118 | + const name = data.name; | |
| 119 | + return manager.savePreset(name, data); | |
| 120 | + }, | |
| 121 | + isValid: (data) => PresetManager.isPossiblyInstructData(data), | |
| 122 | + }, | |
| 123 | + 'context': { | |
| 124 | + name: 'Context Template', | |
| 125 | + getData: () => { | |
| 126 | + const manager = getPresetManager('context'); | |
| 127 | + const name = manager.getSelectedPresetName(); | |
| 128 | + return manager.getPresetSettings(name); | |
| 129 | + }, | |
| 130 | + setData: (data) => { | |
| 131 | + const manager = getPresetManager('context'); | |
| 132 | + const name = data.name; | |
| 133 | + return manager.savePreset(name, data); | |
| 134 | + }, | |
| 135 | + isValid: (data) => PresetManager.isPossiblyContextData(data), | |
| 136 | + }, | |
| 137 | + 'sysprompt': { | |
| 138 | + name: 'System Prompt', | |
| 139 | + getData: () => { | |
| 140 | + const manager = getPresetManager('sysprompt'); | |
| 141 | + const name = manager.getSelectedPresetName(); | |
| 142 | + return manager.getPresetSettings(name); | |
| 143 | + }, | |
| 144 | + setData: (data) => { | |
| 145 | + const manager = getPresetManager('sysprompt'); | |
| 146 | + const name = data.name; | |
| 147 | + return manager.savePreset(name, data); | |
| 148 | + }, | |
| 149 | + isValid: (data) => PresetManager.isPossiblySystemPromptData(data), | |
| 150 | + }, | |
| 151 | + 'preset': { | |
| 152 | + name: 'Text Completion Preset', | |
| 153 | + getData: () => { | |
| 154 | + const manager = getPresetManager('textgenerationwebui'); | |
| 155 | + const name = manager.getSelectedPresetName(); | |
| 156 | + const data = manager.getPresetSettings(name); | |
| 157 | + data['name'] = name; | |
| 158 | + return data; | |
| 159 | + }, | |
| 160 | + setData: (data) => { | |
| 161 | + const manager = getPresetManager('textgenerationwebui'); | |
| 162 | + const name = data.name; | |
| 163 | + return manager.savePreset(name, data); | |
| 164 | + }, | |
| 165 | + isValid: (data) => PresetManager.isPossiblyTextCompletionData(data), | |
| 166 | + }, | |
| 167 | + }; | |
| 168 | + | |
| 169 | + static isPossiblyInstructData(data) { | |
| 170 | + const instructProps = ['name', 'input_sequence', 'output_sequence']; | |
| 171 | + return data && instructProps.every(prop => Object.keys(data).includes(prop)); | |
| 172 | + } | |
| 173 | + | |
| 174 | + static isPossiblyContextData(data) { | |
| 175 | + const contextProps = ['name', 'story_string']; | |
| 176 | + return data && contextProps.every(prop => Object.keys(data).includes(prop)); | |
| 177 | + } | |
| 178 | + | |
| 179 | + static isPossiblySystemPromptData(data) { | |
| 180 | + const sysPromptProps = ['name', 'content']; | |
| 181 | + return data && sysPromptProps.every(prop => Object.keys(data).includes(prop)); | |
| 182 | + } | |
| 183 | + | |
| 184 | + static isPossiblyTextCompletionData(data) { | |
| 185 | + const textCompletionProps = ['temp', 'top_k', 'top_p', 'rep_pen']; | |
| 186 | + return data && textCompletionProps.every(prop => Object.keys(data).includes(prop)); | |
| 187 | + } | |
| 188 | + | |
| 189 | + /** | |
| 190 | + * Imports master settings from JSON data. | |
| 191 | + * @param {object} data Data to import | |
| 192 | + * @param {string} fileName File name | |
| 193 | + * @returns {Promise<void>} | |
| 194 | + */ | |
| 195 | + static async performMasterImport(data, fileName) { | |
| 196 | + if (!data || typeof data !== 'object') { | |
| 197 | + toastr.error('Invalid data provided for master import'); | |
| 198 | + return; | |
| 199 | + } | |
| 200 | + | |
| 201 | + // Check for legacy file imports | |
| 202 | + // 1. Instruct Template | |
| 203 | + if (this.isPossiblyInstructData(data)) { | |
| 204 | + toastr.info('Importing instruct template...', 'Instruct template detected'); | |
| 205 | + return await getPresetManager('instruct').savePreset(data.name, data); | |
| 206 | + } | |
| 207 | + | |
| 208 | + // 2. Context Template | |
| 209 | + if (this.isPossiblyContextData(data)) { | |
| 210 | + toastr.info('Importing as context template...', 'Context template detected'); | |
| 211 | + return await getPresetManager('context').savePreset(data.name, data); | |
| 212 | + } | |
| 213 | + | |
| 214 | + // 3. System Prompt | |
| 215 | + if (this.isPossiblySystemPromptData(data)) { | |
| 216 | + toastr.info('Importing as system prompt...', 'System prompt detected'); | |
| 217 | + return await getPresetManager('sysprompt').savePreset(data.name, data); | |
| 218 | + } | |
| 219 | + | |
| 220 | + // 4. Text Completion settings | |
| 221 | + if (this.isPossiblyTextCompletionData(data)) { | |
| 222 | + toastr.info('Importing as settings preset...', 'Text Completion settings detected'); | |
| 223 | + return await getPresetManager('textgenerationwebui').savePreset(fileName, data); | |
| 224 | + } | |
| 225 | + | |
| 226 | + const validSections = []; | |
| 227 | + for (const [key, section] of Object.entries(this.masterSections)) { | |
| 228 | + if (key in data && section.isValid(data[key])) { | |
| 229 | + validSections.push(key); | |
| 230 | + } | |
| 231 | + } | |
| 232 | + | |
| 233 | + if (validSections.length === 0) { | |
| 234 | + toastr.error('No valid sections found in imported data'); | |
| 235 | + return; | |
| 236 | + } | |
| 237 | + | |
| 238 | + const sectionNames = validSections.reduce((acc, key) => { | |
| 239 | + acc[key] = { key: key, name: this.masterSections[key].name, preset: data[key]?.name || '' }; | |
| 240 | + return acc; | |
| 241 | + }, {}); | |
| 242 | + | |
| 243 | + const html = $(await renderTemplateAsync('masterImport', { sections: sectionNames })); | |
| 244 | + const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', { | |
| 245 | + okButton: 'Import', | |
| 246 | + cancelButton: 'Cancel', | |
| 247 | + }); | |
| 248 | + | |
| 249 | + const result = await popup.show(); | |
| 250 | + | |
| 251 | + // Import cancelled | |
| 252 | + if (result !== POPUP_RESULT.AFFIRMATIVE) { | |
| 253 | + return; | |
| 254 | + } | |
| 255 | + | |
| 256 | + const importedSections = []; | |
| 257 | + const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get(); | |
| 258 | + | |
| 259 | + if (confirmedSections.length === 0) { | |
| 260 | + toastr.info('No sections selected for import'); | |
| 261 | + return; | |
| 262 | + } | |
| 263 | + | |
| 264 | + for (const section of confirmedSections) { | |
| 265 | + const sectionData = data[section]; | |
| 266 | + const masterSection = this.masterSections[section]; | |
| 267 | + if (sectionData && masterSection) { | |
| 268 | + await masterSection.setData(sectionData); | |
| 269 | + importedSections.push(masterSection.name); | |
| 270 | + } | |
| 271 | + } | |
| 272 | + | |
| 273 | + toastr.success(`Imported ${importedSections.length} settings: ${importedSections.join(', ')}`); | |
| 274 | + } | |
| 275 | + | |
| 276 | + /** | |
| 277 | + * Exports master settings to JSON data. | |
| 278 | + * @returns {Promise<string>} JSON data | |
| 279 | + */ | |
| 280 | + static async performMasterExport() { | |
| 281 | + const sectionNames = Object.entries(this.masterSections).reduce((acc, [key, section]) => { | |
| 282 | + acc[key] = { key: key, name: section.name, checked: key !== 'preset' }; | |
| 283 | + return acc; | |
| 284 | + }, {}); | |
| 285 | + const html = $(await renderTemplateAsync('masterExport', { sections: sectionNames })); | |
| 286 | + | |
| 287 | + const popup = new Popup(html, POPUP_TYPE.CONFIRM, '', { | |
| 288 | + okButton: 'Export', | |
| 289 | + cancelButton: 'Cancel', | |
| 290 | + }); | |
| 291 | + | |
| 292 | + const result = await popup.show(); | |
| 293 | + | |
| 294 | + // Export cancelled | |
| 295 | + if (result !== POPUP_RESULT.AFFIRMATIVE) { | |
| 296 | + return; | |
| 297 | + } | |
| 298 | + | |
| 299 | + const confirmedSections = html.find('input:checked').map((_, el) => el instanceof HTMLInputElement && el.value).get(); | |
| 300 | + const data = {}; | |
| 301 | + | |
| 302 | + if (confirmedSections.length === 0) { | |
| 303 | + toastr.info('No sections selected for export'); | |
| 304 | + return; | |
| 305 | + } | |
| 306 | + | |
| 307 | + for (const section of confirmedSections) { | |
| 308 | + const masterSection = this.masterSections[section]; | |
| 309 | + if (masterSection) { | |
| 310 | + data[section] = masterSection.getData(); | |
| 311 | + } | |
| 312 | + } | |
| 313 | + | |
| 314 | + return JSON.stringify(data, null, 4); | |
| 315 | + } | |
| 316 | + | |
| 105 | 317 | /** |
| 106 | 318 | * Gets all preset names. |
| 107 | 319 | * @returns {string[]} List of preset names |
| @@ -182,6 +394,14 @@ class PresetManager { | ||
| 182 | 394 | } |
| 183 | 395 | |
| 184 | 396 | async savePreset(name, settings) { |
| 397 | + if (this.apiId === 'instruct' && settings) { | |
| 398 | + await checkForSystemPromptInInstructTemplate(name, settings); | |
| 399 | + } | |
| 400 | + | |
| 401 | + if (this.apiId === 'novel' && settings) { | |
| 402 | + settings = convertNovelPreset(settings); | |
| 403 | + } | |
| 404 | + | |
| 185 | 405 | const preset = settings ?? this.getPresetSettings(name); |
| 186 | 406 | |
| 187 | 407 | const response = await fetch('/api/presets/save', { |
| @@ -228,6 +448,10 @@ class PresetManager { | ||
| 228 | 448 | presets = instruct_presets; |
| 229 | 449 | preset_names = instruct_presets.map(x => x.name); |
| 230 | 450 | break; |
| 451 | + case 'sysprompt': | |
| 452 | + presets = system_prompts; | |
| 453 | + preset_names = system_prompts.map(x => x.name); | |
| 454 | + break; | |
| 231 | 455 | default: |
| 232 | 456 | console.warn(`Unknown API ID ${this.apiId}`); |
| 233 | 457 | } |
| @@ -236,11 +460,11 @@ class PresetManager { | ||
| 236 | 460 | } |
| 237 | 461 | |
| 238 | 462 | isKeyedApi() { |
| 239 | 463 | return this.apiId == 'textgenerationwebui' || this.apiId == 'context' || this.apiId == 'instruct'isAdvancedFormatting(); |
| 240 | 464 | } |
| 241 | 465 | |
| 242 | 466 | isAdvancedFormatting() { |
| 243 | 467 | return this.apiId == 'context' || this.apiId == 'instruct' || this.apiId == 'sysprompt'; |
| 244 | 468 | } |
| 245 | 469 | |
| 246 | 470 | updateList(name, preset) { |
| @@ -298,6 +522,11 @@ class PresetManager { | ||
| 298 | 522 | instruct_preset['name'] = name || power_user.instruct.preset; |
| 299 | 523 | return instruct_preset; |
| 300 | 524 | } |
| 525 | + case 'sysprompt': { | |
| 526 | + const sysprompt_preset = structuredClone(power_user.sysprompt); | |
| 527 | + sysprompt_preset['name'] = name || power_user.sysprompt.preset; | |
| 528 | + return sysprompt_preset; | |
| 529 | + } | |
| 301 | 530 | default: |
| 302 | 531 | console.warn(`Unknown API ID ${apiId}`); |
| 303 | 532 | return {}; |
| @@ -601,11 +830,6 @@ export async function initPresetManager() { | ||
| 601 | 830 | return; |
| 602 | 831 | } |
| 603 | 832 | |
| 604 | - // default context preset cannot be deleted | |
| 605 | - if (apiId == 'context' && power_user.default_context === power_user.context.preset) { | |
| 606 | - return; | |
| 607 | - } | |
| 608 | - | |
| 609 | 833 | const headerText = !presetManager.isAdvancedFormatting() ? 'Delete this preset?' : 'Delete this template?'; |
| 610 | 834 | const confirm = await Popup.show.confirm(headerText, 'This action is irreversible and your current settings will be overwritten.'); |
| 611 | 835 | if (!confirm) { |
| @@ -682,4 +906,35 @@ export async function initPresetManager() { | ||
| 682 | 906 | toastr.success(successToast); |
| 683 | 907 | } |
| 684 | 908 | }); |
| 909 | + | |
| 910 | + $('#af_master_import').on('click', () => { | |
| 911 | + $('#af_master_import_file').trigger('click'); | |
| 912 | + }); | |
| 913 | + | |
| 914 | + $('#af_master_import_file').on('change', async function (e) { | |
| 915 | + if (!(e.target instanceof HTMLInputElement)) { | |
| 916 | + return; | |
| 917 | + } | |
| 918 | + const file = e.target.files[0]; | |
| 919 | + | |
| 920 | + if (!file) { | |
| 921 | + return; | |
| 922 | + } | |
| 923 | + | |
| 924 | + const data = await parseJsonFile(file); | |
| 925 | + const fileName = file.name.replace('.json', ''); | |
| 926 | + await PresetManager.performMasterImport(data, fileName); | |
| 927 | + e.target.value = null; | |
| 928 | + }); | |
| 929 | + | |
| 930 | + $('#af_master_export').on('click', async () => { | |
| 931 | + const data = await PresetManager.performMasterExport(); | |
| 932 | + | |
| 933 | + if (!data) { | |
| 934 | + return; | |
| 935 | + } | |
| 936 | + | |
| 937 | + const shortDate = new Date().toISOString().split('T')[0]; | |
| 938 | + download(data, `ST-formatting-${shortDate}.json`, 'application/json'); | |
| 939 | + }); | |
| 685 | 940 | } |
| @@ -0,0 +1,250 @@ | ||
| 1 | +import { saveSettingsDebounced } from '../script.js'; | |
| 2 | +import { callGenericPopup, POPUP_TYPE } from './popup.js'; | |
| 3 | +import { power_user } from './power-user.js'; | |
| 4 | +import { getPresetManager } from './preset-manager.js'; | |
| 5 | +import { SlashCommand } from './slash-commands/SlashCommand.js'; | |
| 6 | +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; | |
| 7 | +import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | |
| 8 | +import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; | |
| 9 | +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | |
| 10 | +import { renderTemplateAsync } from './templates.js'; | |
| 11 | +import { isTrueBoolean, resetScrollHeight } from './utils.js'; | |
| 12 | + | |
| 13 | +export let system_prompts = []; | |
| 14 | + | |
| 15 | +const $enabled = $('#sysprompt_enabled'); | |
| 16 | +const $select = $('#sysprompt_select'); | |
| 17 | +const $content = $('#sysprompt_content'); | |
| 18 | +const $contentBlock = $('#SystemPromptBlock'); | |
| 19 | + | |
| 20 | +async function migrateSystemPromptFromInstructMode() { | |
| 21 | + if ('system_prompt' in power_user.instruct) { | |
| 22 | + const prompt = String(power_user.instruct.system_prompt); | |
| 23 | + delete power_user.instruct.system_prompt; | |
| 24 | + power_user.sysprompt.enabled = power_user.instruct.enabled; | |
| 25 | + power_user.sysprompt.content = prompt; | |
| 26 | + | |
| 27 | + const existingPromptName = system_prompts.find(x => x.content === prompt)?.name; | |
| 28 | + | |
| 29 | + if (existingPromptName) { | |
| 30 | + power_user.sysprompt.name = existingPromptName; | |
| 31 | + } else { | |
| 32 | + const data = { name: `[Migrated] ${power_user.instruct.preset}`, content: prompt }; | |
| 33 | + await getPresetManager('sysprompt')?.savePreset(data.name, data); | |
| 34 | + power_user.sysprompt.name = data.name; | |
| 35 | + } | |
| 36 | + | |
| 37 | + saveSettingsDebounced(); | |
| 38 | + toastr.info('System prompt settings have been moved from the Instruct Mode.', 'Migration notice', { timeOut: 5000 }); | |
| 39 | + } | |
| 40 | +} | |
| 41 | + | |
| 42 | +/** | |
| 43 | + * Loads sysprompt settings from the given data object. | |
| 44 | + * @param {object} data Settings data object. | |
| 45 | + */ | |
| 46 | +export async function loadSystemPrompts(data) { | |
| 47 | + if (data.sysprompt !== undefined) { | |
| 48 | + system_prompts = data.sysprompt; | |
| 49 | + } | |
| 50 | + | |
| 51 | + await migrateSystemPromptFromInstructMode(); | |
| 52 | + toggleSystemPromptDisabledControls(); | |
| 53 | + | |
| 54 | + for (const prompt of system_prompts) { | |
| 55 | + $('<option>').val(prompt.name).text(prompt.name).appendTo($select); | |
| 56 | + } | |
| 57 | + | |
| 58 | + $enabled.prop('checked', power_user.sysprompt.enabled); | |
| 59 | + $select.val(power_user.sysprompt.name); | |
| 60 | + $content.val(power_user.sysprompt.content); | |
| 61 | + if (!CSS.supports('field-sizing', 'content')) { | |
| 62 | + await resetScrollHeight($content); | |
| 63 | + } | |
| 64 | +} | |
| 65 | + | |
| 66 | +/** | |
| 67 | + * Checks if the instruct template has a system prompt and prompts the user to save it as a system prompt. | |
| 68 | + * @param {string} name Name of the instruct template | |
| 69 | + * @param {object} template Instruct template object | |
| 70 | + */ | |
| 71 | +export async function checkForSystemPromptInInstructTemplate(name, template) { | |
| 72 | + if (!template || !name || typeof name !== 'string' || typeof template !== 'object') { | |
| 73 | + return; | |
| 74 | + } | |
| 75 | + if ('system_prompt' in template && template.system_prompt) { | |
| 76 | + const existingName = system_prompts.find(x => x.content === template.system_prompt)?.name; | |
| 77 | + const html = await renderTemplateAsync('migrateInstructPrompt', { prompt: template.system_prompt, existing: existingName }); | |
| 78 | + const confirm = await callGenericPopup(html, POPUP_TYPE.CONFIRM); | |
| 79 | + if (confirm) { | |
| 80 | + const migratedName = `[Migrated] ${name}`; | |
| 81 | + const prompt = { name: migratedName, content: template.system_prompt }; | |
| 82 | + const presetManager = getPresetManager('sysprompt'); | |
| 83 | + await presetManager.savePreset(migratedName, prompt); | |
| 84 | + toastr.success(`System prompt "${migratedName}" has been saved.`); | |
| 85 | + } else { | |
| 86 | + toastr.info('System prompt has been discarded.'); | |
| 87 | + } | |
| 88 | + | |
| 89 | + delete template.system_prompt; | |
| 90 | + } | |
| 91 | +} | |
| 92 | + | |
| 93 | +function toggleSystemPromptDisabledControls() { | |
| 94 | + $enabled.parent().find('i').toggleClass('toggleEnabled', !!power_user.sysprompt.enabled); | |
| 95 | + $contentBlock.toggleClass('disabled', !power_user.sysprompt.enabled); | |
| 96 | +} | |
| 97 | + | |
| 98 | +/** | |
| 99 | + * Sets the system prompt state. | |
| 100 | + * @param {boolean} state System prompt state | |
| 101 | + * @returns {string} Empty string | |
| 102 | + */ | |
| 103 | +function setSystemPromptStateCallback(state) { | |
| 104 | + power_user.sysprompt.enabled = state; | |
| 105 | + $enabled.prop('checked', state); | |
| 106 | + toggleSystemPromptDisabledControls(); | |
| 107 | + saveSettingsDebounced(); | |
| 108 | + return ''; | |
| 109 | +} | |
| 110 | + | |
| 111 | +function toggleSystemPromptCallback(_args, state) { | |
| 112 | + if (!state || typeof state !== 'string') { | |
| 113 | + return String(power_user.sysprompt.enabled); | |
| 114 | + } | |
| 115 | + | |
| 116 | + const newState = isTrueBoolean(state); | |
| 117 | + setSystemPromptStateCallback(newState); | |
| 118 | + return String(power_user.sysprompt.enabled); | |
| 119 | +} | |
| 120 | + | |
| 121 | +function selectSystemPromptCallback(args, name) { | |
| 122 | + if (!power_user.sysprompt.enabled && !isTrueBoolean(args.forceGet)) { | |
| 123 | + return ''; | |
| 124 | + } | |
| 125 | + | |
| 126 | + if (!name) { | |
| 127 | + return power_user.sysprompt.name ?? ''; | |
| 128 | + } | |
| 129 | + | |
| 130 | + const quiet = isTrueBoolean(args?.quiet); | |
| 131 | + const systemPromptNames = system_prompts.map(preset => preset.name); | |
| 132 | + let foundName = systemPromptNames.find(x => x.toLowerCase() === name.toLowerCase()); | |
| 133 | + | |
| 134 | + if (!foundName) { | |
| 135 | + const fuse = new Fuse(systemPromptNames); | |
| 136 | + const result = fuse.search(name); | |
| 137 | + | |
| 138 | + if (result.length === 0) { | |
| 139 | + !quiet && toastr.warning(`System prompt "${name}" not found`); | |
| 140 | + return ''; | |
| 141 | + } | |
| 142 | + | |
| 143 | + foundName = result[0].item; | |
| 144 | + } | |
| 145 | + | |
| 146 | + $select.val(foundName).trigger('input'); | |
| 147 | + !quiet && toastr.success(`System prompt "${foundName}" selected`); | |
| 148 | + return foundName; | |
| 149 | +} | |
| 150 | + | |
| 151 | +export function initSystemPrompts() { | |
| 152 | + $enabled.on('input', function () { | |
| 153 | + power_user.sysprompt.enabled = !!$(this).prop('checked'); | |
| 154 | + toggleSystemPromptDisabledControls(); | |
| 155 | + saveSettingsDebounced(); | |
| 156 | + }); | |
| 157 | + | |
| 158 | + $select.on('change', async function () { | |
| 159 | + if (!power_user.sysprompt.enabled) { | |
| 160 | + $enabled.prop('checked', true).trigger('input'); | |
| 161 | + } | |
| 162 | + | |
| 163 | + const name = String($(this).val()); | |
| 164 | + const prompt = system_prompts.find(p => p.name === name); | |
| 165 | + if (prompt) { | |
| 166 | + $content.val(prompt.content); | |
| 167 | + if (!CSS.supports('field-sizing', 'content')) { | |
| 168 | + await resetScrollHeight($content); | |
| 169 | + } | |
| 170 | + | |
| 171 | + power_user.sysprompt.name = name; | |
| 172 | + power_user.sysprompt.content = prompt.content; | |
| 173 | + } | |
| 174 | + saveSettingsDebounced(); | |
| 175 | + }); | |
| 176 | + | |
| 177 | + $content.on('input', function () { | |
| 178 | + power_user.sysprompt.content = String($(this).val()); | |
| 179 | + saveSettingsDebounced(); | |
| 180 | + }); | |
| 181 | + | |
| 182 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 183 | + name: 'sysprompt', | |
| 184 | + aliases: ['system-prompt'], | |
| 185 | + callback: selectSystemPromptCallback, | |
| 186 | + returns: 'current prompt name', | |
| 187 | + namedArgumentList: [ | |
| 188 | + SlashCommandNamedArgument.fromProps({ | |
| 189 | + name: 'quiet', | |
| 190 | + description: 'Suppress the toast message on prompt change', | |
| 191 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 192 | + defaultValue: 'false', | |
| 193 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 194 | + }), | |
| 195 | + SlashCommandNamedArgument.fromProps({ | |
| 196 | + name: 'forceGet', | |
| 197 | + description: 'Force getting a name even if system prompt is disabled', | |
| 198 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 199 | + defaultValue: 'false', | |
| 200 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 201 | + }), | |
| 202 | + ], | |
| 203 | + unnamedArgumentList: [ | |
| 204 | + SlashCommandArgument.fromProps({ | |
| 205 | + description: 'system prompt name', | |
| 206 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 207 | + enumProvider: () => system_prompts.map(x => new SlashCommandEnumValue(x.name, null, enumTypes.enum, enumIcons.preset)), | |
| 208 | + }), | |
| 209 | + ], | |
| 210 | + helpString: ` | |
| 211 | + <div> | |
| 212 | + Selects a system prompt by name, using fuzzy search to find the closest match. | |
| 213 | + Gets the current system prompt if no name is provided and sysprompt is enabled or <code>forceGet=true</code> is passed. | |
| 214 | + </div> | |
| 215 | + <div> | |
| 216 | + <strong>Example:</strong> | |
| 217 | + <ul> | |
| 218 | + <li> | |
| 219 | + <pre><code class="language-stscript">/sysprompt </code></pre> | |
| 220 | + </li> | |
| 221 | + </ul> | |
| 222 | + </div> | |
| 223 | + `, | |
| 224 | + })); | |
| 225 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 226 | + name: 'sysprompt-on', | |
| 227 | + aliases: ['sysprompt-enable'], | |
| 228 | + callback: () => setSystemPromptStateCallback(true), | |
| 229 | + helpString: 'Enables system prompt.', | |
| 230 | + })); | |
| 231 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 232 | + name: 'sysprompt-off', | |
| 233 | + aliases: ['sysprompt-disable'], | |
| 234 | + callback: () => setSystemPromptStateCallback(false), | |
| 235 | + helpString: 'Disables system prompt', | |
| 236 | + })); | |
| 237 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 238 | + name: 'sysprompt-state', | |
| 239 | + aliases: ['sysprompt-toggle'], | |
| 240 | + helpString: 'Gets the current system prompt state. If an argument is provided, it will set the system prompt state.', | |
| 241 | + unnamedArgumentList: [ | |
| 242 | + SlashCommandArgument.fromProps({ | |
| 243 | + description: 'system prompt state', | |
| 244 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 245 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 246 | + }), | |
| 247 | + ], | |
| 248 | + callback: toggleSystemPromptCallback, | |
| 249 | + })); | |
| 250 | +} | |
| @@ -57,8 +57,8 @@ | ||
| 57 | 57 | <li><tt>{{maxPrompt}}</tt> – <span data-i18n="help_macros_41">max allowed prompt length in tokens = (context size - response length)</span></li> |
| 58 | 58 | <li><tt>{{exampleSeparator}}</tt> – <span data-i18n="help_macros_42">context template example dialogues separator</span></li> |
| 59 | 59 | <li><tt>{{chatStart}}</tt> – <span data-i18n="help_macros_43">context template chat start line</span></li> |
| 60 | 60 | <li><tt>{{systemPrompt}}</tt> – <span data-i18n="help_macros_44">main system prompt content if enabled (either character prompt override if chosenallowed, or instructSystemPromptdefaultSystemPrompt)</span></li> |
| 61 | 61 | <li><tt>{{instructSystemPromptdefaultSystemPrompt}}</tt> – <span data-i18n="help_macros_45">instruct system prompt content</span></li> |
| 62 | 62 | <li><tt>{{instructSystemPromptPrefix}}</tt> – <span data-i18n="help_macros_46">instruct system prompt prefix sequence</span></li> |
| 63 | 63 | <li><tt>{{instructSystemPromptSuffix}}</tt> – <span data-i18n="help_macros_47">instruct system prompt suffix sequence</span></li> |
| 64 | 64 | <li><tt>{{instructUserPrefix}}</tt> – <span data-i18n="help_macros_48">instruct user prefix sequence</span></li> |
| @@ -0,0 +1,17 @@ | ||
| 1 | +<h3> | |
| 2 | + Choose what to export | |
| 3 | +</h3> | |
| 4 | +<div class="flex-container flexFlowColumn justifyLeft"> | |
| 5 | + {{#each sections}} | |
| 6 | + {{#with this}} | |
| 7 | + <label class="checkbox_label"> | |
| 8 | + {{#if checked}} | |
| 9 | + <input type="checkbox" value="{{key}}" checked /> | |
| 10 | + {{else}} | |
| 11 | + <input type="checkbox" value="{{key}}" /> | |
| 12 | + {{/if}} | |
| 13 | + <span data-i18n="{{name}}">{{name}}</span> | |
| 14 | + </label> | |
| 15 | + {{/with}} | |
| 16 | + {{/each}} | |
| 17 | +</div> | |
| @@ -0,0 +1,17 @@ | ||
| 1 | +<h3> | |
| 2 | + Choose what to import | |
| 3 | +</h3> | |
| 4 | +<div class="flex-container flexFlowColumn justifyLeft"> | |
| 5 | + {{#each sections}} | |
| 6 | + {{#with this}} | |
| 7 | + <label class="checkbox_label"> | |
| 8 | + <input type="checkbox" value="{{key}}" checked> | |
| 9 | + <span data-i18n="{{name}}">{{name}}</span> | |
| 10 | + {{#if preset}} | |
| 11 | + <span>–</span> | |
| 12 | + <small>{{preset}}</small> | |
| 13 | + {{/if}} | |
| 14 | + </label> | |
| 15 | + {{/with}} | |
| 16 | + {{/each}} | |
| 17 | +</div> | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +<h3> | |
| 2 | + This instruct template also contains a system prompt. | |
| 3 | +</h3> | |
| 4 | +<div> | |
| 5 | + Would you like to migrate the system prompt from the template? | |
| 6 | +</div> | |
| 7 | +{{#if existing}} | |
| 8 | +<div class="marginTopBot5"> | |
| 9 | + <b>Note:</b> | |
| 10 | + <span>you already have this prompt saved as:</span> | |
| 11 | + <span>{{existing}}</span> | |
| 12 | +</div> | |
| 13 | +{{/if}} | |
| 14 | +<div class="justifyLeft marginTop5"> | |
| 15 | + <div> | |
| 16 | + <small> | |
| 17 | + <b>"Yes"</b> – The prompt will be imported and selected as a current system prompt. | |
| 18 | + </small> | |
| 19 | + | |
| 20 | + </div> | |
| 21 | + <div> | |
| 22 | + <small> | |
| 23 | + <b>"No"</b> – The prompt will be ignored, no changes to the current system prompt. | |
| 24 | + </small> | |
| 25 | + </div> | |
| 26 | +</div> | |
| 27 | +<textarea class="wide100p textarea_compact" rows="10">{{prompt}}</textarea> | |
| @@ -41,11 +41,6 @@ | ||
| 41 | 41 | SillyTavern Documentation Site |
| 42 | 42 | </a> |
| 43 | 43 | </li> |
| 44 | - <li> | |
| 45 | - <a target="_blank" href="https://docs.sillytavern.app/extras/installation/" data-i18n="Extras Installation Guide"> | |
| 46 | - Extras Installation Guide | |
| 47 | - </a> | |
| 48 | - </li> | |
| 49 | 44 | </ul> |
| 50 | 45 | |
| 51 | 46 | <hr> |
| @@ -15,7 +15,7 @@ import { BIAS_CACHE, createNewLogitBiasEntry, displayLogitBias, getLogitBiasList | ||
| 15 | 15 | import { power_user, registerDebugFunction } from './power-user.js'; |
| 16 | 16 | import { getEventSourceStream } from './sse-stream.js'; |
| 17 | 17 | import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer } from './textgen-models.js'; |
| 18 | 18 | import { SENTENCEPIECE_TOKENIZERSENCODE_TOKENIZERS, TEXTGEN_TOKENIZERS, getTextTokens, tokenizers } from './tokenizers.js'; |
| 19 | 19 | import { getSortableDelay, onlyUnique } from './utils.js'; |
| 20 | 20 | |
| 21 | 21 | export { |
| @@ -353,7 +353,7 @@ function getTokenizerForTokenIds() { | ||
| 353 | 353 | return tokenizers.API_CURRENT; |
| 354 | 354 | } |
| 355 | 355 | |
| 356 | 356 | if (SENTENCEPIECE_TOKENIZERSENCODE_TOKENIZERS.includes(power_user.tokenizer)) { |
| 357 | 357 | return power_user.tokenizer; |
| 358 | 358 | } |
| 359 | 359 | |
| @@ -1201,9 +1201,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | ||
| 1201 | 1201 | 'frequency_penalty': settings.freq_pen, |
| 1202 | 1202 | 'presence_penalty': settings.presence_pen, |
| 1203 | 1203 | 'repetition_penalty': settings.rep_pen, |
| 1204 | 1204 | 'seed': settings.seed >= 0 ? settings.seed : undefined, |
| 1205 | 1205 | 'stop': getStoppingStrings(isImpersonate, isContinue), |
| 1206 | - 'temperature': settings.temp, | |
| 1206 | + 'temperature': dynatemp ? (settings.min_temp + settings.max_temp) / 2 : settings.temp, | |
| 1207 | 1207 | 'temperature_last': settings.temperature_last, |
| 1208 | 1208 | 'top_p': settings.top_p, |
| 1209 | 1209 | 'top_k': settings.top_k, |
| @@ -1223,6 +1223,12 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | ||
| 1223 | 1223 | 'guided_json': settings.json_schema, |
| 1224 | 1224 | 'early_stopping': false, // hacks |
| 1225 | 1225 | 'include_stop_str_in_output': false, |
| 1226 | + 'dynatemp_min': dynatemp ? settings.min_temp : undefined, | |
| 1227 | + 'dynatemp_max': dynatemp ? settings.max_temp : undefined, | |
| 1228 | + 'dynatemp_exponent': dynatemp ? settings.dynatemp_exponent : undefined, | |
| 1229 | + 'xtc_threshold': settings.xtc_threshold, | |
| 1230 | + 'xtc_probability': settings.xtc_probability, | |
| 1231 | + 'custom_token_bans': toIntArray(banned_tokens), | |
| 1226 | 1232 | }; |
| 1227 | 1233 | |
| 1228 | 1234 | if (settings.type === OPENROUTER) { |
| @@ -10,7 +10,8 @@ import { getCurrentDreamGenModelTokenizer, getCurrentOpenRouterModelTokenizer, o | ||
| 10 | 10 | const { OOBA, TABBY, KOBOLDCPP, VLLM, APHRODITE, LLAMACPP, OPENROUTER, DREAMGEN } = textgen_types; |
| 11 | 11 | |
| 12 | 12 | export const CHARACTERS_PER_TOKEN_RATIO = 3.35; |
| 13 | 13 | export const TOKENIZER_WARNING_KEY = 'tokenizationWarningShown'; |
| 14 | +export const TOKENIZER_SUPPORTED_KEY = 'tokenizationSupported'; | |
| 14 | 15 | |
| 15 | 16 | export const tokenizers = { |
| 16 | 17 | NONE: 0, |
| @@ -30,21 +31,27 @@ export const tokenizers = { | ||
| 30 | 31 | JAMBA: 14, |
| 31 | 32 | QWEN2: 15, |
| 32 | 33 | COMMAND_R: 16, |
| 34 | + NEMO: 17, | |
| 33 | 35 | BEST_MATCH: 99, |
| 34 | 36 | }; |
| 35 | 37 | |
| 36 | -export const SENTENCEPIECE_TOKENIZERS = [ | |
| 38 | +// A list of local tokenizers that support encoding and decoding token ids. | |
| 39 | +export const ENCODE_TOKENIZERS = [ | |
| 37 | 40 | tokenizers.LLAMA, |
| 38 | 41 | tokenizers.MISTRAL, |
| 39 | 42 | tokenizers.YI, |
| 40 | 43 | tokenizers.LLAMA3, |
| 41 | 44 | tokenizers.GEMMA, |
| 42 | 45 | tokenizers.JAMBA, |
| 46 | + tokenizers.QWEN2, | |
| 47 | + tokenizers.COMMAND_R, | |
| 48 | + tokenizers.NEMO, | |
| 43 | 49 | // uncomment when NovelAI releases Kayra and Clio weights, lol |
| 44 | 50 | //tokenizers.NERD, |
| 45 | 51 | //tokenizers.NERD2, |
| 46 | 52 | ]; |
| 47 | 53 | |
| 54 | +// A list of Text Completion sources that support remote tokenization. | |
| 48 | 55 | export const TEXTGEN_TOKENIZERS = [OOBA, TABBY, KOBOLDCPP, LLAMACPP, VLLM, APHRODITE]; |
| 49 | 56 | |
| 50 | 57 | const TOKENIZER_URLS = { |
| @@ -117,6 +124,11 @@ const TOKENIZER_URLS = { | ||
| 117 | 124 | decode: '/api/tokenizers/command-r/decode', |
| 118 | 125 | count: '/api/tokenizers/command-r/encode', |
| 119 | 126 | }, |
| 127 | + [tokenizers.NEMO]: { | |
| 128 | + encode: '/api/tokenizers/nemo/encode', | |
| 129 | + decode: '/api/tokenizers/nemo/decode', | |
| 130 | + count: '/api/tokenizers/nemo/encode', | |
| 131 | + }, | |
| 120 | 132 | [tokenizers.API_TEXTGENERATIONWEBUI]: { |
| 121 | 133 | encode: '/api/tokenizers/remote/textgenerationwebui/encode', |
| 122 | 134 | count: '/api/tokenizers/remote/textgenerationwebui/encode', |
| @@ -262,6 +274,9 @@ export function getTokenizerBestMatch(forApi) { | ||
| 262 | 274 | if (nai_settings.model_novel.includes('kayra')) { |
| 263 | 275 | return tokenizers.NERD2; |
| 264 | 276 | } |
| 277 | + if (nai_settings.model_novel.includes('erato')) { | |
| 278 | + return tokenizers.LLAMA3; | |
| 279 | + } | |
| 265 | 280 | } |
| 266 | 281 | if (forApi === 'kobold' || forApi === 'textgenerationwebui' || forApi === 'koboldhorde') { |
| 267 | 282 | // Try to use the API tokenizer if possible: |
| @@ -269,8 +284,9 @@ export function getTokenizerBestMatch(forApi) { | ||
| 269 | 284 | // - Kobold must pass a version check |
| 270 | 285 | // - Tokenizer haven't reported an error previously |
| 271 | 286 | const hasTokenizerError = sessionStorage.getItem(TOKENIZER_WARNING_KEY); |
| 287 | + const hasValidEndpoint = sessionStorage.getItem(TOKENIZER_SUPPORTED_KEY); | |
| 272 | 288 | const isConnected = online_status !== 'no_connection'; |
| 273 | 289 | const isTokenizerSupported = TEXTGEN_TOKENIZERS.includes(textgen_settings.type) && (textgen_settings.type !== OOBA || hasValidEndpoint); |
| 274 | 290 | |
| 275 | 291 | if (!hasTokenizerError && isConnected) { |
| 276 | 292 | if (forApi === 'kobold' && kai_flags.can_use_tokenization) { |
| @@ -531,6 +547,7 @@ export function getTokenizerModel() { | ||
| 531 | 547 | const jambaTokenizer = 'jamba'; |
| 532 | 548 | const qwen2Tokenizer = 'qwen2'; |
| 533 | 549 | const commandRTokenizer = 'command-r'; |
| 550 | + const nemoTokenizer = 'nemo'; | |
| 534 | 551 | |
| 535 | 552 | // Assuming no one would use it for different models.. right? |
| 536 | 553 | if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { |
| @@ -624,6 +641,9 @@ export function getTokenizerModel() { | ||
| 624 | 641 | } |
| 625 | 642 | |
| 626 | 643 | if (oai_settings.chat_completion_source == chat_completion_sources.MISTRALAI) { |
| 644 | + if (oai_settings.mistralai_model.includes('nemo') || oai_settings.mistralai_model.includes('pixtral')) { | |
| 645 | + return nemoTokenizer; | |
| 646 | + } | |
| 627 | 647 | return mistralTokenizer; |
| 628 | 648 | } |
| 629 | 649 | |
| @@ -908,15 +928,20 @@ function countTokensFromTextgenAPI(str, resolve) { | ||
| 908 | 928 | |
| 909 | 929 | function apiFailureTokenCount(str) { |
| 910 | 930 | console.error('Error counting tokens'); |
| 931 | + let shouldTryAgain = false; | |
| 911 | 932 | |
| 912 | 933 | if (!sessionStorage.getItem(TOKENIZER_WARNING_KEY)) { |
| 913 | - toastr.warning( | |
| 934 | + const bestMatchBefore = getTokenizerBestMatch(main_api); | |
| 914 | - 'Your selected API doesn\'t support the tokenization endpoint. Using estimated counts.', | |
| 915 | - 'Error counting tokens', | |
| 916 | - { timeOut: 10000, preventDuplicates: true }, | |
| 917 | - ); | |
| 918 | - | |
| 919 | 935 | sessionStorage.setItem(TOKENIZER_WARNING_KEY, String(true)); |
| 936 | + const bestMatchAfter = getTokenizerBestMatch(main_api); | |
| 937 | + if ([tokenizers.API_TEXTGENERATIONWEBUI, tokenizers.API_KOBOLD].includes(bestMatchBefore) && bestMatchBefore !== bestMatchAfter) { | |
| 938 | + shouldTryAgain = true; | |
| 939 | + } | |
| 940 | + } | |
| 941 | + | |
| 942 | + // Only try again if we guarantee not to be looped by the same error | |
| 943 | + if (shouldTryAgain && power_user.tokenizer === tokenizers.BEST_MATCH) { | |
| 944 | + return getTokenCount(str); | |
| 920 | 945 | } |
| 921 | 946 | |
| 922 | 947 | return guesstimate(str); |
| @@ -607,12 +607,11 @@ export function sortByCssOrder(a, b) { | ||
| 607 | 607 | /** |
| 608 | 608 | * Trims a string to the end of a nearest sentence. |
| 609 | 609 | * @param {string} input The string to trim. |
| 610 | - * @param {boolean} include_newline Whether to include a newline character in the trimmed string. | |
| 611 | 610 | * @returns {string} The trimmed string. |
| 612 | 611 | * @example |
| 613 | 612 | * trimToEndSentence('Hello, world! I am from'); // 'Hello, world!' |
| 614 | 613 | */ |
| 615 | 614 | export function trimToEndSentence(input, include_newline = false) { |
| 616 | 615 | if (!input) { |
| 617 | 616 | return ''; |
| 618 | 617 | } |
| @@ -633,11 +632,6 @@ export function trimToEndSentence(input, include_newline = false) { | ||
| 633 | 632 | } |
| 634 | 633 | break; |
| 635 | 634 | } |
| 636 | - | |
| 637 | - if (include_newline && char === '\n') { | |
| 638 | - last = i; | |
| 639 | - break; | |
| 640 | - } | |
| 641 | 635 | } |
| 642 | 636 | |
| 643 | 637 | if (last === -1) { |
| @@ -1908,13 +1902,13 @@ export function select2ChoiceClickSubscribe(control, action, { buttonStyle = fal | ||
| 1908 | 1902 | * @returns {string} The html representation of the highlighted regex |
| 1909 | 1903 | */ |
| 1910 | 1904 | export function highlightRegex(regexStr) { |
| 1911 | 1905 | // Function to escape HTML special characters for safety or readability |
| 1912 | 1906 | const escapeHtmlescape = (str) => str.replace(/[&<>"'\x01]/g, match => ({ |
| 1913 | 1907 | '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''', '\x01': '\\x01', |
| 1914 | 1908 | })[match]); |
| 1915 | 1909 | |
| 1916 | 1910 | // Replace special characters with their HTML-escaped forms |
| 1917 | 1911 | regexStr = escapeHtmlescape(regexStr); |
| 1918 | 1912 | |
| 1919 | 1913 | // Patterns that we want to highlight only if they are not escaped |
| 1920 | 1914 | function getPatterns() { |
| @@ -217,8 +217,9 @@ class WorldInfoBuffer { | ||
| 217 | 217 | depth = MAX_SCAN_DEPTH; |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | 220 | const JOINERMATCHER = '\n\x01'; |
| 221 | - let result = this.#depthBuffer.slice(this.#startDepth, depth).join(JOINER); | |
| 221 | + const JOINER = '\n' + MATCHER; | |
| 222 | + let result = MATCHER + this.#depthBuffer.slice(this.#startDepth, depth).join(JOINER); | |
| 222 | 223 | |
| 223 | 224 | if (this.#injectBuffer.length > 0) { |
| 224 | 225 | result += JOINER + this.#injectBuffer.join(JOINER); |
| @@ -2949,16 +2950,39 @@ async function getWorldEntry(name, data, entry) { | ||
| 2949 | 2950 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); |
| 2950 | 2951 | |
| 2951 | 2952 | // delay until recursion |
| 2953 | + // delay until recursion level | |
| 2952 | 2954 | const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]'); |
| 2953 | 2955 | delayUntilRecursionInput.data('uid', entry.uid); |
| 2956 | + const delayUntilRecursionLevelInput = template.find('input[name="delayUntilRecursionLevel"]'); | |
| 2957 | + delayUntilRecursionLevelInput.data('uid', entry.uid); | |
| 2954 | 2958 | delayUntilRecursionInput.on('input', async function () { |
| 2955 | 2959 | const uid = $(this).data('uid'); |
| 2956 | 2960 | const valuetoggled = $(this).prop('checked'); |
| 2961 | + | |
| 2962 | + // If the value contains a number, we'll take that one (set by the level input), otherwise we can use true/false switch | |
| 2963 | + const value = toggled ? data.entries[uid].delayUntilRecursion || true : false; | |
| 2964 | + | |
| 2965 | + if (!toggled) delayUntilRecursionLevelInput.val(''); | |
| 2966 | + | |
| 2957 | 2967 | data.entries[uid].delayUntilRecursion = value; |
| 2958 | 2968 | setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); |
| 2959 | 2969 | await saveWorldInfo(name, data); |
| 2960 | 2970 | }); |
| 2961 | 2971 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); |
| 2972 | + delayUntilRecursionLevelInput.on('input', async function () { | |
| 2973 | + const uid = $(this).data('uid'); | |
| 2974 | + const content = $(this).val(); | |
| 2975 | + const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true) | |
| 2976 | + : content === 1 ? true | |
| 2977 | + : !isNaN(Number(content)) ? Number(content) | |
| 2978 | + : false; | |
| 2979 | + | |
| 2980 | + data.entries[uid].delayUntilRecursion = value; | |
| 2981 | + setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); | |
| 2982 | + await saveWorldInfo(name, data); | |
| 2983 | + }); | |
| 2984 | + // No need to retrigger inpout event, we'll just set the curret current value. It was edited/saved above already | |
| 2985 | + delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input'); | |
| 2962 | 2986 | |
| 2963 | 2987 | // duplicate button |
| 2964 | 2988 | const duplicateButton = template.find('.duplicate_entry_button'); |
| @@ -3257,7 +3281,7 @@ export const newWorldInfoEntryDefinition = { | ||
| 3257 | 3281 | disable: { default: false, type: 'boolean' }, |
| 3258 | 3282 | excludeRecursion: { default: false, type: 'boolean' }, |
| 3259 | 3283 | preventRecursion: { default: false, type: 'boolean' }, |
| 3260 | 3284 | delayUntilRecursion: { default: false0, type: 'booleannumber' }, |
| 3261 | 3285 | probability: { default: 100, type: 'number' }, |
| 3262 | 3286 | useProbability: { default: true, type: 'boolean' }, |
| 3263 | 3287 | depth: { default: DEFAULT_DEPTH, type: 'number' }, |
| @@ -3696,6 +3720,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3696 | 3720 | } |
| 3697 | 3721 | } |
| 3698 | 3722 | |
| 3723 | + /** @type {scan_state} */ | |
| 3699 | 3724 | let scanState = scan_state.INITIAL; |
| 3700 | 3725 | let token_budget_overflowed = false; |
| 3701 | 3726 | let count = 0; |
| @@ -3720,6 +3745,17 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3720 | 3745 | return { worldInfoBefore: '', worldInfoAfter: '', WIDepthEntries: [], EMEntries: [], allActivatedEntries: new Set() }; |
| 3721 | 3746 | } |
| 3722 | 3747 | |
| 3748 | + /** @type {number[]} Represents the delay levels for entries that are delayed until recursion */ | |
| 3749 | + const availableRecursionDelayLevels = [...new Set(sortedEntries | |
| 3750 | + .filter(entry => entry.delayUntilRecursion) | |
| 3751 | + .map(entry => entry.delayUntilRecursion === true ? 1 : entry.delayUntilRecursion), | |
| 3752 | + )].sort((a, b) => a - b); | |
| 3753 | + // Already preset with the first level | |
| 3754 | + let currentRecursionDelayLevel = availableRecursionDelayLevels.shift() ?? 0; | |
| 3755 | + if (currentRecursionDelayLevel > 0 && availableRecursionDelayLevels.length) { | |
| 3756 | + console.debug('[WI] Preparing first delayed recursion level', currentRecursionDelayLevel, '. Still delayed:', availableRecursionDelayLevels); | |
| 3757 | + } | |
| 3758 | + | |
| 3723 | 3759 | console.debug(`[WI] --- SEARCHING ENTRIES (on ${sortedEntries.length} entries) ---`); |
| 3724 | 3760 | |
| 3725 | 3761 | while (scanState) { |
| @@ -3732,7 +3768,8 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3732 | 3768 | // Track how many times the loop has run. May be useful for debugging. |
| 3733 | 3769 | count++; |
| 3734 | 3770 | |
| 3735 | 3771 | console.debug(`[WI] Loop--- LOOP #${count}. SearchSTART state---`, Object.entries(scan_state).find(x => x[1] === scanState)); |
| 3772 | + console.debug('[WI] Scan state', Object.entries(scan_state).find(x => x[1] === scanState)); | |
| 3736 | 3773 | |
| 3737 | 3774 | // Until decided otherwise, we set the loop to stop scanning after this |
| 3738 | 3775 | let nextScanState = scan_state.NONE; |
| @@ -3811,6 +3848,11 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3811 | 3848 | continue; |
| 3812 | 3849 | } |
| 3813 | 3850 | |
| 3851 | + if (scanState === scan_state.RECURSION && entry.delayUntilRecursion && entry.delayUntilRecursion > currentRecursionDelayLevel && !isSticky) { | |
| 3852 | + log('suppressed by delay until recursion level', entry.delayUntilRecursion, '. Currently', currentRecursionDelayLevel); | |
| 3853 | + continue; | |
| 3854 | + } | |
| 3855 | + | |
| 3814 | 3856 | if (scanState === scan_state.RECURSION && world_info_recursive && entry.excludeRecursion && !isSticky) { |
| 3815 | 3857 | log('suppressed by exclude recursion'); |
| 3816 | 3858 | continue; |
| @@ -3949,6 +3991,8 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3949 | 3991 | filterByInclusionGroups(newEntries, allActivatedEntries, buffer, scanState, timedEffects); |
| 3950 | 3992 | |
| 3951 | 3993 | console.debug('[WI] --- PROBABILITY CHECKS ---'); |
| 3994 | + !newEntries.length && console.debug('[WI] No probability checks to do'); | |
| 3995 | + | |
| 3952 | 3996 | for (const entry of newEntries) { |
| 3953 | 3997 | function verifyProbability() { |
| 3954 | 3998 | // If we don't need to roll, it's always true |
| @@ -3984,6 +4028,7 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 3984 | 4028 | newContent += `${entry.content}\n`; |
| 3985 | 4029 | |
| 3986 | 4030 | if ((textToScanTokens + (await getTokenCountAsync(newContent))) >= budget) { |
| 4031 | + console.debug('[WI] --- BUDGET OVERFLOW CHECK ---'); | |
| 3987 | 4032 | if (world_info_overflow_alert) { |
| 3988 | 4033 | console.warn(`[WI] budget of ${budget} reached, stopping after ${allActivatedEntries.size} entries`); |
| 3989 | 4034 | toastr.warning(`World info budget reached after ${allActivatedEntries.size} entries.`, 'World Info'); |
| @@ -4001,23 +4046,31 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 4001 | 4046 | const successfulNewEntries = newEntries.filter(x => !failedProbabilityChecks.has(x)); |
| 4002 | 4047 | const successfulNewEntriesForRecursion = successfulNewEntries.filter(x => !x.preventRecursion); |
| 4003 | 4048 | |
| 4049 | + console.debug(`[WI] --- LOOP #${count} RESULT ---`); | |
| 4004 | 4050 | if (!newEntries.length) { |
| 4005 | 4051 | console.debug('[WI] No new entries activated, stopping.'); |
| 4006 | 4052 | } else if (!successfulNewEntries.length) { |
| 4007 | 4053 | console.debug('[WI] Probability checks failed for all activated entries,. stoppingNo new entries activated.'); |
| 4008 | 4054 | } else { |
| 4009 | 4055 | console.debug(`[WI] Successfully activated ${successfulNewEntries.length} new entries to prompt. ${allActivatedEntries.size} total entries activated.`, successfulNewEntries); |
| 4010 | 4056 | } |
| 4011 | 4057 | |
| 4058 | + function logNextState(...args) { | |
| 4059 | + args.length && console.debug(args.shift(), ...args); | |
| 4060 | + console.debug('[WI] Setting scan state', Object.entries(scan_state).find(x => x[1] === scanState)); | |
| 4061 | + } | |
| 4062 | + | |
| 4012 | 4063 | // After processing and rolling entries is done, see if we should continue with normal recursion |
| 4013 | 4064 | if (world_info_recursive && !token_budget_overflowed && successfulNewEntriesForRecursion.length) { |
| 4014 | 4065 | nextScanState = scan_state.RECURSION; |
| 4066 | + logNextState('[WI] Found', successfulNewEntriesForRecursion.length, 'new entries for recursion'); | |
| 4015 | 4067 | } |
| 4016 | 4068 | |
| 4017 | 4069 | // If we are inside min activations scan, and we have recursive buffer, we should do a recursive scan before increasing the buffer again |
| 4018 | 4070 | // There might be recurse-trigger-able entries that match the buffer, so we need to check that |
| 4019 | 4071 | if (world_info_recursive && !token_budget_overflowed && scanState === scan_state.MIN_ACTIVATIONS && buffer.hasRecurse()) { |
| 4020 | 4072 | nextScanState = scan_state.RECURSION; |
| 4073 | + logNextState('[WI] Min Activations run done, whill will always be followed by a recursive scan'); | |
| 4021 | 4074 | } |
| 4022 | 4075 | |
| 4023 | 4076 | // If scanning is planned to stop, but min activations is set and not satisfied, check if we should continue |
| @@ -4031,14 +4084,21 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 4031 | 4084 | ) || (buffer.getDepth() > chat.length); |
| 4032 | 4085 | |
| 4033 | 4086 | if (!over_max) { |
| 4034 | - console.debug(`[WI] Min activations not reached (${allActivatedEntries.size}/${world_info_min_activations}), advancing depth to ${buffer.getDepth() + 1} and checking again`); | |
| 4035 | 4087 | nextScanState = scan_state.MIN_ACTIVATIONS; // loop |
| 4088 | + logNextState(`[WI] Min activations not reached (${allActivatedEntries.size}/${world_info_min_activations}), advancing depth to ${buffer.getDepth() + 1}, starting another scan`); | |
| 4036 | 4089 | buffer.advanceScan(); |
| 4037 | 4090 | } else { |
| 4038 | 4091 | console.debug(`[WI] Min activations not reached (${allActivatedEntries.size}/${world_info_min_activations}), but reached on of depth. Stopping`); |
| 4039 | 4092 | } |
| 4040 | 4093 | } |
| 4041 | 4094 | |
| 4095 | + // If the scan is done, but we still have open "delay until recursion" levels, we should continue with the next one | |
| 4096 | + if (nextScanState === scan_state.NONE && availableRecursionDelayLevels.length) { | |
| 4097 | + nextScanState = scan_state.RECURSION; | |
| 4098 | + currentRecursionDelayLevel = availableRecursionDelayLevels.shift(); | |
| 4099 | + logNextState('[WI] Open delayed recursion levels left. Preparing next delayed recursion level', currentRecursionDelayLevel, '. Still delayed:', availableRecursionDelayLevels); | |
| 4100 | + } | |
| 4101 | + | |
| 4042 | 4102 | // Final check if we should really continue scan, and extend the current WI recurse buffer |
| 4043 | 4103 | scanState = nextScanState; |
| 4044 | 4104 | if (scanState) { |
| @@ -4046,6 +4106,8 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 4046 | 4106 | .map(x => x.content).join('\n'); |
| 4047 | 4107 | buffer.addRecurse(text); |
| 4048 | 4108 | allActivatedText = (text + '\n' + allActivatedText); |
| 4109 | + } else { | |
| 4110 | + logNextState('[WI] Scan done. No new entries to prompt. Stopping.'); | |
| 4049 | 4111 | } |
| 4050 | 4112 | } |
| 4051 | 4113 | |
| @@ -4072,10 +4134,10 @@ export async function checkWorldInfo(chat, maxContext, isDryRun) { | ||
| 4072 | 4134 | |
| 4073 | 4135 | switch (entry.position) { |
| 4074 | 4136 | case world_info_position.before: |
| 4075 | 4137 | WIBeforeEntries.unshift(substituteParams(content)); |
| 4076 | 4138 | break; |
| 4077 | 4139 | case world_info_position.after: |
| 4078 | 4140 | WIAfterEntries.unshift(substituteParams(content)); |
| 4079 | 4141 | break; |
| 4080 | 4142 | case world_info_position.EMTop: |
| 4081 | 4143 | EMEntries.unshift( |
| @@ -2570,20 +2570,25 @@ input[type="file"] { | ||
| 2570 | 2570 | /*#################################################################*/ |
| 2571 | 2571 | |
| 2572 | 2572 | select { |
| 2573 | - | |
| 2573 | + -moz-appearance: none; | |
| 2574 | - padding: 3px 2px; | |
| 2574 | + -webkit-appearance: none; | |
| 2575 | + appearance: none; | |
| 2576 | + padding: 3px 5px; | |
| 2575 | 2577 | background-color: var(--black30a); |
| 2576 | 2578 | border: 1px solid var(--SmartThemeBorderColor); |
| 2577 | 2579 | border-radius: 5px; |
| 2578 | 2580 | margin-bottom: 5px; |
| 2579 | 2581 | height: min-content; |
| 2582 | + background-image: url('/img/down-arrow.svg'); | |
| 2583 | + background-repeat: no-repeat; | |
| 2584 | + background-position: right 6px center; | |
| 2585 | + background-size: 8px 5px; | |
| 2580 | 2586 | } |
| 2581 | 2587 | |
| 2582 | 2588 | select option { |
| 2583 | 2589 | /* works to highlight selected/active option */ |
| 2584 | 2590 | background-color: var(--white50a); |
| 2585 | 2591 | color: var(--black70a); |
| 2586 | - | |
| 2587 | 2592 | } |
| 2588 | 2593 | |
| 2589 | 2594 | select option:not(:checked) { |
| @@ -2625,14 +2630,6 @@ select option:not(:checked) { | ||
| 2625 | 2630 | color: var(--active) !important; |
| 2626 | 2631 | } |
| 2627 | 2632 | |
| 2628 | -#context_set_default.default { | |
| 2629 | - color: var(--preferred) !important; | |
| 2630 | -} | |
| 2631 | - | |
| 2632 | -#instruct_set_default.default { | |
| 2633 | - color: var(--preferred) !important; | |
| 2634 | -} | |
| 2635 | - | |
| 2636 | 2633 | .displayBlock { |
| 2637 | 2634 | display: block !important; |
| 2638 | 2635 | } |
| @@ -3518,6 +3515,14 @@ grammarly-extension { | ||
| 3518 | 3515 | column-gap: 20px; |
| 3519 | 3516 | } |
| 3520 | 3517 | |
| 3518 | +.horde-model-description { | |
| 3519 | + -webkit-line-clamp: 3; | |
| 3520 | + line-clamp: 3; | |
| 3521 | + font-size: 0.9em; | |
| 3522 | + overflow: hidden; | |
| 3523 | + text-overflow: ellipsis; | |
| 3524 | +} | |
| 3525 | + | |
| 3521 | 3526 | .drag-handle { |
| 3522 | 3527 | cursor: grab; |
| 3523 | 3528 | /* Make the drag handle not selectable in most browsers */ |
| @@ -5464,3 +5469,12 @@ body:not(.movingUI) .drawer-content.maximized { | ||
| 5464 | 5469 | #AdvancedFormatting .autoSetHeight { |
| 5465 | 5470 | overflow-wrap: anywhere; |
| 5466 | 5471 | } |
| 5472 | + | |
| 5473 | +#InstructSequencesColumn summary { | |
| 5474 | + font-size: 0.95em; | |
| 5475 | + cursor: pointer; | |
| 5476 | +} | |
| 5477 | + | |
| 5478 | +#InstructSequencesColumn details:not(:last-of-type) { | |
| 5479 | + margin-bottom: 5px; | |
| 5480 | +} | |
| @@ -610,6 +610,9 @@ app.use('/api/search', require('./src/endpoints/search').router); | ||
| 610 | 610 | // Ooba/OpenAI text completions |
| 611 | 611 | app.use('/api/backends/text-completions', require('./src/endpoints/backends/text-completions').router); |
| 612 | 612 | |
| 613 | +// OpenRouter | |
| 614 | +app.use('/api/openrouter', require('./src/endpoints/openrouter').router); | |
| 615 | + | |
| 613 | 616 | // KoboldAI |
| 614 | 617 | app.use('/api/backends/kobold', require('./src/endpoints/backends/kobold').router); |
| 615 | 618 | |
| @@ -929,6 +932,7 @@ async function verifySecuritySettings() { | ||
| 929 | 932 | userModule.initUserStorage(dataRoot) |
| 930 | 933 | .then(userModule.ensurePublicDirectoriesExist) |
| 931 | 934 | .then(userModule.migrateUserData) |
| 935 | + .then(userModule.migrateSystemPrompts) | |
| 932 | 936 | .then(verifySecuritySettings) |
| 933 | 937 | .then(preSetupTasks) |
| 934 | 938 | .finally(startServer); |
| @@ -5,7 +5,6 @@ const PUBLIC_DIRECTORIES = { | ||
| 5 | 5 | extensions: 'public/scripts/extensions', |
| 6 | 6 | }; |
| 7 | 7 | |
| 8 | -const DEFAULT_AVATAR = '/img/ai4.png'; | |
| 9 | 8 | const SETTINGS_FILE = 'settings.json'; |
| 10 | 9 | |
| 11 | 10 | /** |
| @@ -42,6 +41,7 @@ const USER_DIRECTORY_TEMPLATE = Object.freeze({ | ||
| 42 | 41 | files: 'user/files', |
| 43 | 42 | vectors: 'vectors', |
| 44 | 43 | backups: 'backups', |
| 44 | + sysprompt: 'sysprompt', | |
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | 47 | /** |
| @@ -422,7 +422,6 @@ const VLLM_KEYS = [ | ||
| 422 | 422 | |
| 423 | 423 | module.exports = { |
| 424 | 424 | DEFAULT_USER, |
| 425 | - DEFAULT_AVATAR, | |
| 426 | 425 | SETTINGS_FILE, |
| 427 | 426 | PUBLIC_DIRECTORIES, |
| 428 | 427 | USER_DIRECTORY_TEMPLATE, |
| @@ -186,6 +186,7 @@ router.post('/status', jsonParser, async function (request, response) { | ||
| 186 | 186 | |
| 187 | 187 | const modelName = modelInfo?.model_name; |
| 188 | 188 | result = modelName || result; |
| 189 | + response.setHeader('x-supports-tokenization', 'true'); | |
| 189 | 190 | } |
| 190 | 191 | } catch (error) { |
| 191 | 192 | console.error(`Failed to get Ooba model info: ${error}`); |
| @@ -726,13 +726,12 @@ router.post('/create', urlencodedParser, async function (request, response) { | ||
| 726 | 726 | const char = JSON.stringify(charaFormatData(request.body, request.user.directories)); |
| 727 | 727 | const internalName = getPngName(request.body.ch_name, request.user.directories); |
| 728 | 728 | const avatarName = `${internalName}.png`; |
| 729 | - const defaultAvatar = './public/img/ai4.png'; | |
| 730 | 729 | const chatsPath = path.join(request.user.directories.chats, internalName); |
| 731 | 730 | |
| 732 | 731 | if (!fs.existsSync(chatsPath)) fs.mkdirSync(chatsPath); |
| 733 | 732 | |
| 734 | 733 | if (!request.file) { |
| 735 | 734 | await writeCharacterData(defaultAvatardefaultAvatarPath, char, internalName, request); |
| 736 | 735 | return response.send(avatarName); |
| 737 | 736 | } else { |
| 738 | 737 | const crop = tryParse(request.query.crop); |
| @@ -1028,6 +1027,12 @@ router.post('/chats', jsonParser, async function (request, response) { | ||
| 1028 | 1027 | const stats = fs.statSync(pathToFile); |
| 1029 | 1028 | const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`; |
| 1030 | 1029 | |
| 1030 | + if (stats.size === 0) { | |
| 1031 | + console.log(`Found an empty chat file: ${pathToFile}`); | |
| 1032 | + res({}); | |
| 1033 | + return; | |
| 1034 | + } | |
| 1035 | + | |
| 1031 | 1036 | const rl = readline.createInterface({ |
| 1032 | 1037 | input: fileStream, |
| 1033 | 1038 | crlfDelay: Infinity, |
| @@ -4,6 +4,7 @@ const readline = require('readline'); | ||
| 4 | 4 | const express = require('express'); |
| 5 | 5 | const sanitize = require('sanitize-filename'); |
| 6 | 6 | const writeFileAtomicSync = require('write-file-atomic').sync; |
| 7 | +const _ = require('lodash'); | |
| 7 | 8 | |
| 8 | 9 | const { jsonParser, urlencodedParser } = require('../express-common'); |
| 9 | 10 | const { getConfigValue, humanizedISO8601DateTime, tryParse, generateTimestamp, removeOldBackups } = require('../util'); |
| @@ -34,6 +35,27 @@ function backupChat(directory, name, chat) { | ||
| 34 | 35 | } |
| 35 | 36 | } |
| 36 | 37 | |
| 38 | +const backupFunctions = new Map(); | |
| 39 | + | |
| 40 | +/** | |
| 41 | + * Gets a backup function for a user. | |
| 42 | + * @param {string} handle User handle | |
| 43 | + * @returns {function(string, string, string): void} Backup function | |
| 44 | + */ | |
| 45 | +function getBackupFunction(handle) { | |
| 46 | + const throttleInterval = getConfigValue('chatBackupThrottleInterval', 10_000); | |
| 47 | + if (!backupFunctions.has(handle)) { | |
| 48 | + backupFunctions.set(handle, _.throttle(backupChat, throttleInterval, { leading: true, trailing: true })); | |
| 49 | + } | |
| 50 | + return backupFunctions.get(handle); | |
| 51 | +} | |
| 52 | + | |
| 53 | +process.on('exit', () => { | |
| 54 | + for (const func of backupFunctions.values()) { | |
| 55 | + func.flush(); | |
| 56 | + } | |
| 57 | +}); | |
| 58 | + | |
| 37 | 59 | /** |
| 38 | 60 | * Imports a chat from Ooba's format. |
| 39 | 61 | * @param {string} userName User name |
| @@ -70,8 +92,7 @@ function importOobaChat(userName, characterName, jsonData) { | ||
| 70 | 92 | } |
| 71 | 93 | } |
| 72 | 94 | |
| 73 | 95 | const chatContent =return chat.map(obj => JSON.stringify(obj)).join('\n'); |
| 74 | - return chatContent; | |
| 75 | 96 | } |
| 76 | 97 | |
| 77 | 98 | /** |
| @@ -99,8 +120,7 @@ function importAgnaiChat(userName, characterName, jsonData) { | ||
| 99 | 120 | }); |
| 100 | 121 | } |
| 101 | 122 | |
| 102 | 123 | const chatContent =return chat.map(obj => JSON.stringify(obj)).join('\n'); |
| 103 | - return chatContent; | |
| 104 | 124 | } |
| 105 | 125 | |
| 106 | 126 | /** |
| @@ -137,6 +157,37 @@ function importCAIChat(userName, characterName, jsonData) { | ||
| 137 | 157 | return newChats; |
| 138 | 158 | } |
| 139 | 159 | |
| 160 | +/** | |
| 161 | + * Flattens `msg` and `swipes` data from Chub Chat format. | |
| 162 | + * Only changes enough to make it compatible with the standard chat serialization format. | |
| 163 | + * @param {string} userName User name | |
| 164 | + * @param {string} characterName Character name | |
| 165 | + * @param {string[]} lines serialised JSONL data | |
| 166 | + * @returns {string} Converted data | |
| 167 | + */ | |
| 168 | +function flattenChubChat(userName, characterName, lines) { | |
| 169 | + function flattenSwipe(swipe) { | |
| 170 | + return swipe.message ? swipe.message : swipe; | |
| 171 | + } | |
| 172 | + | |
| 173 | + function convert(line) { | |
| 174 | + const lineData = tryParse(line); | |
| 175 | + if (!lineData) return line; | |
| 176 | + | |
| 177 | + if (lineData.mes && lineData.mes.message) { | |
| 178 | + lineData.mes = lineData?.mes.message; | |
| 179 | + } | |
| 180 | + | |
| 181 | + if (lineData?.swipes && Array.isArray(lineData.swipes)) { | |
| 182 | + lineData.swipes = lineData.swipes.map(swipe => flattenSwipe(swipe)); | |
| 183 | + } | |
| 184 | + | |
| 185 | + return JSON.stringify(lineData); | |
| 186 | + } | |
| 187 | + | |
| 188 | + return (lines ?? []).map(convert).join('\n'); | |
| 189 | +} | |
| 190 | + | |
| 140 | 191 | const router = express.Router(); |
| 141 | 192 | |
| 142 | 193 | router.post('/save', jsonParser, function (request, response) { |
| @@ -144,10 +195,10 @@ router.post('/save', jsonParser, function (request, response) { | ||
| 144 | 195 | const directoryName = String(request.body.avatar_url).replace('.png', ''); |
| 145 | 196 | const chatData = request.body.chat; |
| 146 | 197 | const jsonlData = chatData.map(JSON.stringify).join('\n'); |
| 147 | 198 | const fileName = `${sanitize(String(request.body.file_name))}.jsonl`; |
| 148 | 199 | const filePath = path.join(request.user.directories.chats, directoryName, sanitize(fileName)); |
| 149 | 200 | writeFileAtomicSync(filePath, jsonlData, 'utf8'); |
| 150 | 201 | backupChatgetBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData); |
| 151 | 202 | return response.send({ result: 'ok' }); |
| 152 | 203 | } catch (error) { |
| 153 | 204 | response.send(error); |
| @@ -171,14 +222,15 @@ router.post('/get', jsonParser, function (request, response) { | ||
| 171 | 222 | return response.send({}); |
| 172 | 223 | } |
| 173 | 224 | |
| 174 | 225 | const fileName = path.join(directoryPath, `${sanitize(String(request.body.file_name))}.jsonl`); |
| 175 | 226 | const chatFileExistsfilePath = fspath.existsSyncjoin(directoryPath, sanitize(fileName)); |
| 227 | + const chatFileExists = fs.existsSync(filePath); | |
| 176 | 228 | |
| 177 | 229 | if (!chatFileExists) { |
| 178 | 230 | return response.send({}); |
| 179 | 231 | } |
| 180 | 232 | |
| 181 | 233 | const data = fs.readFileSync(fileNamefilePath, 'utf8'); |
| 182 | 234 | const lines = data.split('\n'); |
| 183 | 235 | |
| 184 | 236 | // Iterate through the array of strings and parse each line as JSON |
| @@ -199,8 +251,9 @@ router.post('/rename', jsonParser, async function (request, response) { | ||
| 199 | 251 | const pathToFolder = request.body.is_group |
| 200 | 252 | ? request.user.directories.groupChats |
| 201 | 253 | : path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', '')); |
| 202 | 254 | const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file)); |
| 203 | 255 | const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file)); |
| 256 | + const sanitizedFileName = path.parse(pathToRenamedFile).name; | |
| 204 | 257 | console.log('Old chat name', pathToOriginalFile); |
| 205 | 258 | console.log('New chat name', pathToRenamedFile); |
| 206 | 259 | |
| @@ -212,32 +265,22 @@ router.post('/rename', jsonParser, async function (request, response) { | ||
| 212 | 265 | fs.copyFileSync(pathToOriginalFile, pathToRenamedFile); |
| 213 | 266 | fs.rmSync(pathToOriginalFile); |
| 214 | 267 | console.log('Successfully renamed.'); |
| 215 | 268 | return response.send({ ok: true, sanitizedFileName }); |
| 216 | 269 | }); |
| 217 | 270 | |
| 218 | 271 | router.post('/delete', jsonParser, function (request, response) { |
| 219 | - if (!request.body) { | |
| 220 | - console.log('no request body seen'); | |
| 221 | - return response.sendStatus(400); | |
| 222 | - } | |
| 223 | - | |
| 224 | - if (request.body.chatfile !== sanitize(request.body.chatfile)) { | |
| 225 | - console.error('Malicious chat name prevented'); | |
| 226 | - return response.sendStatus(403); | |
| 227 | - } | |
| 228 | - | |
| 229 | 272 | const dirName = String(request.body.avatar_url).replace('.png', ''); |
| 230 | 273 | const fileName = path.join(request.user.directories.chats, dirName, sanitize(String(request.body.chatfile))); |
| 231 | 274 | const chatFileExistsfilePath = fspath.existsSyncjoin(request.user.directories.chats, dirName, sanitize(fileName)); |
| 275 | + const chatFileExists = fs.existsSync(filePath); | |
| 232 | 276 | |
| 233 | 277 | if (!chatFileExists) { |
| 234 | 278 | console.log(`Chat file not found '${fileNamefilePath}'`); |
| 235 | 279 | return response.sendStatus(400); |
| 236 | - } else { | |
| 237 | - fs.rmSync(fileName); | |
| 238 | - console.log('Deleted chat file: ' + fileName); | |
| 239 | 280 | } |
| 240 | 281 | |
| 282 | + fs.rmSync(filePath); | |
| 283 | + console.log('Deleted chat file: ' + filePath); | |
| 241 | 284 | return response.send('ok'); |
| 242 | 285 | }); |
| 243 | 286 | |
| @@ -259,7 +302,7 @@ router.post('/export', jsonParser, async function (request, response) { | ||
| 259 | 302 | } |
| 260 | 303 | try { |
| 261 | 304 | // Short path for JSONL files |
| 262 | 305 | if (request.body.format === 'jsonl') { |
| 263 | 306 | try { |
| 264 | 307 | const rawFile = fs.readFileSync(filename, 'utf8'); |
| 265 | 308 | const successMessage = { |
| @@ -269,8 +312,7 @@ router.post('/export', jsonParser, async function (request, response) { | ||
| 269 | 312 | |
| 270 | 313 | console.log(`Chat exported as ${exportfilename}`); |
| 271 | 314 | return response.status(200).json(successMessage); |
| 272 | - } | |
| 315 | + } catch (err) { | |
| 273 | - catch (err) { | |
| 274 | 316 | console.error(err); |
| 275 | 317 | const errorMessage = { |
| 276 | 318 | message: `Could not read JSONL file to export. Source chat file: ${filename}.`, |
| @@ -305,8 +347,7 @@ router.post('/export', jsonParser, async function (request, response) { | ||
| 305 | 347 | console.log(`Chat exported as ${exportfilename}`); |
| 306 | 348 | return response.status(200).json(successMessage); |
| 307 | 349 | }); |
| 308 | - } | |
| 350 | + } catch (err) { | |
| 309 | - catch (err) { | |
| 310 | 351 | console.log('chat export failed.'); |
| 311 | 352 | console.log(err); |
| 312 | 353 | return response.sendStatus(400); |
| @@ -382,20 +423,36 @@ router.post('/import', urlencodedParser, function (request, response) { | ||
| 382 | 423 | } |
| 383 | 424 | |
| 384 | 425 | if (format === 'jsonl') { |
| 385 | 426 | constlet linelines = data.split('\n')[0]; |
| 427 | + const header = lines[0]; | |
| 428 | + | |
| 429 | + const jsonData = JSON.parse(header); | |
| 430 | + | |
| 431 | + if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) { | |
| 432 | + console.log('Incorrect chat format .jsonl'); | |
| 433 | + return response.send({ error: true }); | |
| 434 | + } | |
| 386 | 435 | |
| 387 | - const jsonData = JSON.parse(line); | |
| 436 | + // Do a tiny bit of work to import Chub Chat data | |
| 437 | + // Processing the entire file is so fast that it's not worth checking if it's a Chub chat first | |
| 438 | + let flattenedChat = data; | |
| 439 | + try { | |
| 440 | + // flattening is unlikely to break, but it's not worth failing to | |
| 441 | + // import normal chats in an attempt to import a Chub chat | |
| 442 | + flattenedChat = flattenChubChat(userName, characterName, lines); | |
| 443 | + } catch (error) { | |
| 444 | + console.warn('Failed to flatten Chub Chat data: ', error); | |
| 445 | + } | |
| 388 | 446 | |
| 389 | - if (jsonData.user_name !== undefined || jsonData.name !== undefined) { | |
| 390 | 447 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; |
| 391 | 448 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); |
| 449 | + if (flattenedChat !== data) { | |
| 450 | + writeFileAtomicSync(filePath, flattenedChat, 'utf8'); | |
| 451 | + } else { | |
| 392 | 452 | fs.copyFileSync(pathToUpload, filePath); |
| 453 | + } | |
| 393 | 454 | fs.unlinkSync(pathToUpload); |
| 394 | 455 | response.send({ res: true }); |
| 395 | - } else { | |
| 396 | - console.log('Incorrect chat format .jsonl'); | |
| 397 | - return response.send({ error: true }); | |
| 398 | - } | |
| 399 | 456 | } |
| 400 | 457 | } catch (error) { |
| 401 | 458 | console.error(error); |
| @@ -454,7 +511,7 @@ router.post('/group/save', jsonParser, (request, response) => { | ||
| 454 | 511 | let chat_data = request.body.chat; |
| 455 | 512 | let jsonlData = chat_data.map(JSON.stringify).join('\n'); |
| 456 | 513 | writeFileAtomicSync(pathToFile, jsonlData, 'utf8'); |
| 457 | 514 | backupChatgetBackupFunction(request.user.profile.handle)(request.user.directories.backups, String(id), jsonlData); |
| 458 | 515 | return response.send({ ok: true }); |
| 459 | 516 | }); |
| 460 | 517 | |
| @@ -43,6 +43,7 @@ const CONTENT_TYPES = { | ||
| 43 | 43 | CONTEXT: 'context', |
| 44 | 44 | MOVING_UI: 'moving_ui', |
| 45 | 45 | QUICK_REPLIES: 'quick_replies', |
| 46 | + SYSPROMPT: 'sysprompt', | |
| 46 | 47 | }; |
| 47 | 48 | |
| 48 | 49 | /** |
| @@ -56,7 +57,7 @@ function getDefaultPresets(directories) { | ||
| 56 | 57 | const presets = []; |
| 57 | 58 | |
| 58 | 59 | for (const contentItem of contentIndex) { |
| 59 | 60 | if (contentItem.type.endsWith('_preset') || contentItem.type === 'instruct' || contentItem.type === 'context' || contentItem.type === 'sysprompt') { |
| 60 | 61 | contentItem.name = path.parse(contentItem.filename).name; |
| 61 | 62 | contentItem.folder = getTargetByType(contentItem.type, directories); |
| 62 | 63 | presets.push(contentItem); |
| @@ -218,6 +219,41 @@ function getContentIndex() { | ||
| 218 | 219 | } |
| 219 | 220 | |
| 220 | 221 | /** |
| 222 | + * Gets content by type and format. | |
| 223 | + * @param {string} type Type of content | |
| 224 | + * @param {'json'|'string'|'raw'} format Format of content | |
| 225 | + * @returns {string[]|Buffer[]} Array of content | |
| 226 | + */ | |
| 227 | +function getContentOfType(type, format) { | |
| 228 | + const contentIndex = getContentIndex(); | |
| 229 | + const indexItems = contentIndex.filter((item) => item.type === type && item.folder); | |
| 230 | + const files = []; | |
| 231 | + for (const item of indexItems) { | |
| 232 | + if (!item.folder) { | |
| 233 | + continue; | |
| 234 | + } | |
| 235 | + try { | |
| 236 | + const filePath = path.join(item.folder, item.filename); | |
| 237 | + const fileContent = fs.readFileSync(filePath); | |
| 238 | + switch (format) { | |
| 239 | + case 'json': | |
| 240 | + files.push(JSON.parse(fileContent.toString())); | |
| 241 | + break; | |
| 242 | + case 'string': | |
| 243 | + files.push(fileContent.toString()); | |
| 244 | + break; | |
| 245 | + case 'raw': | |
| 246 | + files.push(fileContent); | |
| 247 | + break; | |
| 248 | + } | |
| 249 | + } catch { | |
| 250 | + // Ignore errors | |
| 251 | + } | |
| 252 | + } | |
| 253 | + return files; | |
| 254 | +} | |
| 255 | + | |
| 256 | +/** | |
| 221 | 257 | * Gets the target directory for the specified asset type. |
| 222 | 258 | * @param {ContentType} type Asset type |
| 223 | 259 | * @param {import('../users').UserDirectoryList} directories User directories |
| @@ -257,6 +293,8 @@ function getTargetByType(type, directories) { | ||
| 257 | 293 | return directories.movingUI; |
| 258 | 294 | case CONTENT_TYPES.QUICK_REPLIES: |
| 259 | 295 | return directories.quickreplies; |
| 296 | + case CONTENT_TYPES.SYSPROMPT: | |
| 297 | + return directories.sysprompt; | |
| 260 | 298 | default: |
| 261 | 299 | return null; |
| 262 | 300 | } |
| @@ -721,5 +759,6 @@ module.exports = { | ||
| 721 | 759 | checkForNewContent, |
| 722 | 760 | getDefaultPresets, |
| 723 | 761 | getDefaultPresetFile, |
| 762 | + getContentOfType, | |
| 724 | 763 | router, |
| 725 | 764 | }; |
| @@ -6,6 +6,7 @@ const { readSecret, SECRET_KEYS } = require('./secrets'); | ||
| 6 | 6 | const { jsonParser } = require('../express-common'); |
| 7 | 7 | |
| 8 | 8 | const ANONYMOUS_KEY = '0000000000'; |
| 9 | +const HORDE_TEXT_MODEL_METADATA_URL = 'https://raw.githubusercontent.com/db0/AI-Horde-text-model-reference/main/db.json'; | |
| 9 | 10 | const cache = new Cache(60 * 1000); |
| 10 | 11 | const router = express.Router(); |
| 11 | 12 | |
| @@ -23,10 +24,9 @@ async function getClientAgent() { | ||
| 23 | 24 | * @returns {Promise<AIHorde>} AIHorde client |
| 24 | 25 | */ |
| 25 | 26 | async function getHordeClient() { |
| 26 | 27 | const ai_horde =return new AIHorde({ |
| 27 | 28 | client_agent: await getClientAgent(), |
| 28 | 29 | }); |
| 29 | - return ai_horde; | |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | /** |
| @@ -79,10 +79,24 @@ router.post('/text-workers', jsonParser, async (request, response) => { | ||
| 79 | 79 | } |
| 80 | 80 | }); |
| 81 | 81 | |
| 82 | +async function getHordeTextModelMetadata() { | |
| 83 | + const response = await fetch(HORDE_TEXT_MODEL_METADATA_URL); | |
| 84 | + return await response.json(); | |
| 85 | +} | |
| 86 | + | |
| 87 | +async function mergeModelsAndMetadata(models, metadata) { | |
| 88 | + return models.map(model => { | |
| 89 | + const metadataModel = metadata[model.name]; | |
| 90 | + if (!metadataModel) { | |
| 91 | + return { ...model, is_whitelisted: false }; | |
| 92 | + } | |
| 93 | + return { ...model, ...metadataModel, is_whitelisted: true }; | |
| 94 | + }); | |
| 95 | +} | |
| 96 | + | |
| 82 | 97 | router.post('/text-models', jsonParser, async (request, response) => { |
| 83 | 98 | try { |
| 84 | 99 | const cachedModels = cache.get('models'); |
| 85 | - | |
| 86 | 100 | if (cachedModels && !request.body.force) { |
| 87 | 101 | return response.send(cachedModels); |
| 88 | 102 | } |
| @@ -94,7 +108,17 @@ router.post('/text-models', jsonParser, async (request, response) => { | ||
| 94 | 108 | }, |
| 95 | 109 | }); |
| 96 | 110 | |
| 97 | 111 | constlet data = await fetchResult.json(); |
| 112 | + | |
| 113 | + // attempt to fetch and merge models metadata | |
| 114 | + try { | |
| 115 | + const metadata = await getHordeTextModelMetadata(); | |
| 116 | + data = await mergeModelsAndMetadata(data, metadata); | |
| 117 | + } | |
| 118 | + catch (error) { | |
| 119 | + console.error('Failed to fetch metadata:', error); | |
| 120 | + } | |
| 121 | + | |
| 98 | 122 | cache.set('models', data); |
| 99 | 123 | return response.send(data); |
| 100 | 124 | } catch (error) { |
| @@ -310,6 +334,7 @@ router.post('/generate-image', jsonParser, async (request, response) => { | ||
| 310 | 334 | console.log('Stable Horde request:', request.body); |
| 311 | 335 | |
| 312 | 336 | const ai_horde = await getHordeClient(); |
| 337 | + // noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan | |
| 313 | 338 | const generation = await ai_horde.postAsyncImageGenerate( |
| 314 | 339 | { |
| 315 | 340 | prompt: `${request.body.prompt} ### ${request.body.negative_prompt}`, |
| @@ -15,6 +15,11 @@ const badWordsList = [ | ||
| 15 | 15 | [19438], [43145], [26523], [41471], [2936], [85, 85], [49332], [7286], [1115], [24], |
| 16 | 16 | ]; |
| 17 | 17 | |
| 18 | +const eratoBadWordsList = [ | |
| 19 | + [16067], [933, 11144], [25106, 11144], [58, 106901, 16073, 33710, 25, 109933], | |
| 20 | + [933, 58, 11144], [128030], [58, 30591, 33503, 17663, 100204, 25, 11144], | |
| 21 | +]; | |
| 22 | + | |
| 18 | 23 | const hypeBotBadWordsList = [ |
| 19 | 24 | [58], [60], [90], [92], [685], [1391], [1782], [2361], [3693], [4083], [4357], [4895], |
| 20 | 25 | [5512], [5974], [7131], [8183], [8351], [8762], [8964], [8973], [9063], [11208], |
| @@ -42,12 +47,24 @@ const repPenaltyAllowList = [ | ||
| 42 | 47 | 803, 1040, 49209, 4, 5, 6, 7, 8, 9, 10, 11, 12], |
| 43 | 48 | ]; |
| 44 | 49 | |
| 50 | +const eratoRepPenWhitelist = [ | |
| 51 | + 6, 1, 11, 13, 25, 198, 12, 9, 8, 279, 264, 459, 323, 477, 539, 912, 374, 574, 1051, 1550, 1587, 4536, 5828, 15058, | |
| 52 | + 3287, 3250, 1461, 1077, 813, 11074, 872, 1202, 1436, 7846, 1288, 13434, 1053, 8434, 617, 9167, 1047, 19117, 706, | |
| 53 | + 12775, 649, 4250, 527, 7784, 690, 2834, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1210, 1359, 608, 220, 596, 956, | |
| 54 | + 3077, 44886, 4265, 3358, 2351, 2846, 311, 389, 315, 304, 520, 505, 430 | |
| 55 | +]; | |
| 56 | + | |
| 45 | 57 | // Ban the dinkus and asterism |
| 46 | 58 | const logitBiasExp = [ |
| 47 | 59 | { 'sequence': [23], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false }, |
| 48 | 60 | { 'sequence': [21], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false }, |
| 49 | 61 | ]; |
| 50 | 62 | |
| 63 | +const eratoLogitBiasExp = [ | |
| 64 | + { 'sequence': [12488], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false }, | |
| 65 | + { 'sequence': [128041], 'bias': -0.08, 'ensure_sequence_finish': false, 'generate_once': false }, | |
| 66 | +]; | |
| 67 | + | |
| 51 | 68 | function getBadWordsList(model) { |
| 52 | 69 | let list = []; |
| 53 | 70 | |
| @@ -59,10 +76,40 @@ function getBadWordsList(model) { | ||
| 59 | 76 | list = badWordsList; |
| 60 | 77 | } |
| 61 | 78 | |
| 79 | + if (model.includes('erato')) { | |
| 80 | + list = eratoBadWordsList; | |
| 81 | + } | |
| 82 | + | |
| 62 | 83 | // Clone the list so we don't modify the original |
| 63 | 84 | return list.slice(); |
| 64 | 85 | } |
| 65 | 86 | |
| 87 | +function getLogitBiasList(model) { | |
| 88 | + let list = []; | |
| 89 | + | |
| 90 | + if (model.includes('erato')) { | |
| 91 | + list = eratoLogitBiasExp; | |
| 92 | + } | |
| 93 | + | |
| 94 | + if (model.includes('clio') || model.includes('kayra')) { | |
| 95 | + list = logitBiasExp; | |
| 96 | + } | |
| 97 | + | |
| 98 | + return list.slice(); | |
| 99 | +} | |
| 100 | + | |
| 101 | +function getRepPenaltyWhitelist(model) { | |
| 102 | + if (model.includes('clio') || model.includes('kayra')) { | |
| 103 | + return repPenaltyAllowList.flat(); | |
| 104 | + } | |
| 105 | + | |
| 106 | + if (model.includes('erato')) { | |
| 107 | + return eratoRepPenWhitelist.flat(); | |
| 108 | + } | |
| 109 | + | |
| 110 | + return null; | |
| 111 | +} | |
| 112 | + | |
| 66 | 113 | const router = express.Router(); |
| 67 | 114 | |
| 68 | 115 | router.post('/status', jsonParser, async function (req, res) { |
| @@ -116,11 +163,10 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 116 | 163 | controller.abort(); |
| 117 | 164 | }); |
| 118 | 165 | |
| 119 | - const isNewModel = (req.body.model.includes('clio') || req.body.model.includes('kayra')); | |
| 166 | + // Add customized bad words for Clio, Kayra, and Erato | |
| 120 | 167 | const badWordsList = getBadWordsList(req.body.model); |
| 121 | 168 | |
| 122 | - // Add customized bad words for Clio and Kayra | |
| 169 | + if (Array.isArray(badWordsList) && Array.isArray(req.body.bad_words_ids)) { | |
| 123 | - if (isNewModel && Array.isArray(req.body.bad_words_ids)) { | |
| 124 | 170 | for (const badWord of req.body.bad_words_ids) { |
| 125 | 171 | if (Array.isArray(badWord) && badWord.every(x => Number.isInteger(x))) { |
| 126 | 172 | badWordsList.push(badWord); |
| @@ -136,12 +182,14 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 136 | 182 | } |
| 137 | 183 | |
| 138 | 184 | // Add default biases for dinkus and asterism |
| 139 | - const logit_bias_exp = isNewModel ? logitBiasExp.slice() : []; | |
| 185 | + const logitBiasList = getLogitBiasList(req.body.model); | |
| 140 | 186 | |
| 141 | 187 | if (Array.isArray(logit_bias_explogitBiasList) && Array.isArray(req.body.logit_bias_exp)) { |
| 142 | 188 | logit_bias_explogitBiasList.push(...req.body.logit_bias_exp); |
| 143 | 189 | } |
| 144 | 190 | |
| 191 | + const repPenWhitelist = getRepPenaltyWhitelist(req.body.model); | |
| 192 | + | |
| 145 | 193 | const data = { |
| 146 | 194 | 'input': req.body.input, |
| 147 | 195 | 'model': req.body.model, |
| @@ -156,32 +204,39 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 156 | 204 | 'repetition_penalty_slope': req.body.repetition_penalty_slope, |
| 157 | 205 | 'repetition_penalty_frequency': req.body.repetition_penalty_frequency, |
| 158 | 206 | 'repetition_penalty_presence': req.body.repetition_penalty_presence, |
| 159 | 207 | 'repetition_penalty_whitelist': isNewModel ? repPenaltyAllowList.flat() : nullrepPenWhitelist, |
| 160 | 208 | 'top_a': req.body.top_a, |
| 161 | 209 | 'top_p': req.body.top_p, |
| 162 | 210 | 'top_k': req.body.top_k, |
| 163 | 211 | 'typical_p': req.body.typical_p, |
| 164 | 212 | 'mirostat_lr': req.body.mirostat_lr, |
| 165 | 213 | 'mirostat_tau': req.body.mirostat_tau, |
| 166 | - 'cfg_scale': req.body.cfg_scale, | |
| 167 | - 'cfg_uc': req.body.cfg_uc, | |
| 168 | 214 | 'phrase_rep_pen': req.body.phrase_rep_pen, |
| 169 | 215 | 'stop_sequences': req.body.stop_sequences, |
| 170 | 216 | 'bad_words_ids': badWordsList.length ? badWordsList : null, |
| 171 | 217 | 'logit_bias_exp': logit_bias_explogitBiasList, |
| 172 | 218 | 'generate_until_sentence': req.body.generate_until_sentence, |
| 173 | 219 | 'use_cache': req.body.use_cache, |
| 174 | 220 | 'return_full_text': req.body.return_full_text, |
| 175 | 221 | 'prefix': req.body.prefix, |
| 176 | 222 | 'order': req.body.order, |
| 177 | 223 | 'num_logprobs': req.body.num_logprobs, |
| 224 | + 'min_p': req.body.min_p, | |
| 225 | + 'math1_temp': req.body.math1_temp, | |
| 226 | + 'math1_quad': req.body.math1_quad, | |
| 227 | + 'math1_quad_entropy_scale': req.body.math1_quad_entropy_scale, | |
| 178 | 228 | }, |
| 179 | 229 | }; |
| 180 | 230 | |
| 181 | 231 | // Tells the model to stop generation at '>' |
| 182 | 232 | if ('theme_textadventure' === req.body.prefix && isNewModel) { |
| 233 | + if (req.body.model.includes('clio') || req.body.model.includes('kayra')) { | |
| 183 | 234 | data.parameters.eos_token_id = 49405; |
| 184 | 235 | } |
| 236 | + if (req.body.model.includes('erato')) { | |
| 237 | + data.parameters.eos_token_id = 29; | |
| 238 | + } | |
| 239 | + } | |
| 185 | 240 | |
| 186 | 241 | console.log(util.inspect(data, { depth: 4 })); |
| 187 | 242 | |
| @@ -192,7 +247,7 @@ router.post('/generate', jsonParser, async function (req, res) { | ||
| 192 | 247 | }; |
| 193 | 248 | |
| 194 | 249 | try { |
| 195 | 250 | const baseURL = (req.body.model.includes('kayra') || req.body.model.includes('erato')) ? TEXT_NOVELAI : API_NOVELAI; |
| 196 | 251 | const url = req.body.streaming ? `${baseURL}/ai/generate-stream` : `${baseURL}/ai/generate`; |
| 197 | 252 | const response = await fetch(url, { method: 'POST', timeout: 0, ...args }); |
| 198 | 253 | |
| @@ -51,6 +51,10 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 51 | 51 | key = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | + if (request.body.api === 'mistral') { | |
| 55 | + key = readSecret(request.user.directories, SECRET_KEYS.MISTRALAI); | |
| 56 | + } | |
| 57 | + | |
| 54 | 58 | if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) { |
| 55 | 59 | console.log('No key found for API', request.body.api); |
| 56 | 60 | return response.sendStatus(400); |
| @@ -107,6 +111,10 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 107 | 111 | apiUrl = 'https://api.01.ai/v1/chat/completions'; |
| 108 | 112 | } |
| 109 | 113 | |
| 114 | + if (request.body.api === 'mistral') { | |
| 115 | + apiUrl = 'https://api.mistral.ai/v1/chat/completions'; | |
| 116 | + } | |
| 117 | + | |
| 110 | 118 | if (request.body.api === 'ooba') { |
| 111 | 119 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 112 | 120 | const imgMessage = body.messages.pop(); |
| @@ -0,0 +1,32 @@ | ||
| 1 | +const express = require('express'); | |
| 2 | +const { jsonParser } = require('../express-common'); | |
| 3 | + | |
| 4 | +const router = express.Router(); | |
| 5 | +const API_OPENROUTER = 'https://openrouter.ai/api/v1'; | |
| 6 | + | |
| 7 | +router.post('/models/multimodal', jsonParser, async (_req, res) => { | |
| 8 | + try { | |
| 9 | + // The endpoint is available without authentication | |
| 10 | + const response = await fetch(`${API_OPENROUTER}/models`, { | |
| 11 | + method: 'GET', | |
| 12 | + headers: { | |
| 13 | + 'Accept': 'application/json', | |
| 14 | + }, | |
| 15 | + }); | |
| 16 | + | |
| 17 | + if (!response.ok) { | |
| 18 | + return res.json([]); | |
| 19 | + } | |
| 20 | + | |
| 21 | + const data = await response.json(); | |
| 22 | + const models = data?.data || []; | |
| 23 | + const multimodalModels = models.filter(m => m?.architecture?.modality === 'text+image->text').map(m => m.id); | |
| 24 | + | |
| 25 | + return res.json(multimodalModels); | |
| 26 | + } catch (error) { | |
| 27 | + console.error(error); | |
| 28 | + return res.sendStatus(500); | |
| 29 | + } | |
| 30 | +}); | |
| 31 | + | |
| 32 | +module.exports = { router }; | |
| @@ -27,6 +27,8 @@ function getPresetSettingsByAPI(apiId, directories) { | ||
| 27 | 27 | return { folder: directories.instruct, extension: '.json' }; |
| 28 | 28 | case 'context': |
| 29 | 29 | return { folder: directories.context, extension: '.json' }; |
| 30 | + case 'sysprompt': | |
| 31 | + return { folder: directories.sysprompt, extension: '.json' }; | |
| 30 | 32 | default: |
| 31 | 33 | return { folder: null, extension: null }; |
| 32 | 34 | } |
| @@ -251,6 +251,7 @@ router.post('/get', jsonParser, (request, response) => { | ||
| 251 | 251 | |
| 252 | 252 | const instruct = readAndParseFromDirectory(request.user.directories.instruct); |
| 253 | 253 | const context = readAndParseFromDirectory(request.user.directories.context); |
| 254 | + const sysprompt = readAndParseFromDirectory(request.user.directories.sysprompt); | |
| 254 | 255 | |
| 255 | 256 | response.send({ |
| 256 | 257 | settings, |
| @@ -268,6 +269,7 @@ router.post('/get', jsonParser, (request, response) => { | ||
| 268 | 269 | quickReplyPresets, |
| 269 | 270 | instruct, |
| 270 | 271 | context, |
| 272 | + sysprompt, | |
| 271 | 273 | enable_extensions: ENABLE_EXTENSIONS, |
| 272 | 274 | enable_extensions_auto_update: ENABLE_EXTENSIONS_AUTO_UPDATE, |
| 273 | 275 | enable_accounts: ENABLE_ACCOUNTS, |
| @@ -221,6 +221,7 @@ const claude_tokenizer = new WebTokenizer('src/tokenizers/claude.json'); | ||
| 221 | 221 | const llama3_tokenizer = new WebTokenizer('src/tokenizers/llama3.json'); |
| 222 | 222 | const commandTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/command-r.json', 'src/tokenizers/llama3.json'); |
| 223 | 223 | const qwen2Tokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/qwen2.json', 'src/tokenizers/llama3.json'); |
| 224 | +const nemoTokenizer = new WebTokenizer('https://github.com/SillyTavern/SillyTavern-Tokenizers/raw/main/nemo.json', 'src/tokenizers/llama3.json'); | |
| 224 | 225 | |
| 225 | 226 | const sentencepieceTokenizers = [ |
| 226 | 227 | 'llama', |
| @@ -418,6 +419,10 @@ function getTokenizerModel(requestModel) { | ||
| 418 | 419 | return 'command-r'; |
| 419 | 420 | } |
| 420 | 421 | |
| 422 | + if (requestModel.includes('nemo')) { | |
| 423 | + return 'nemo'; | |
| 424 | + } | |
| 425 | + | |
| 421 | 426 | // default |
| 422 | 427 | return 'gpt-3.5-turbo'; |
| 423 | 428 | } |
| @@ -645,6 +650,7 @@ router.post('/claude/encode', jsonParser, createWebTokenizerEncodingHandler(clau | ||
| 645 | 650 | router.post('/llama3/encode', jsonParser, createWebTokenizerEncodingHandler(llama3_tokenizer)); |
| 646 | 651 | router.post('/qwen2/encode', jsonParser, createWebTokenizerEncodingHandler(qwen2Tokenizer)); |
| 647 | 652 | router.post('/command-r/encode', jsonParser, createWebTokenizerEncodingHandler(commandTokenizer)); |
| 653 | +router.post('/nemo/encode', jsonParser, createWebTokenizerEncodingHandler(nemoTokenizer)); | |
| 648 | 654 | router.post('/llama/decode', jsonParser, createSentencepieceDecodingHandler(spp_llama)); |
| 649 | 655 | router.post('/nerdstash/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd)); |
| 650 | 656 | router.post('/nerdstash_v2/decode', jsonParser, createSentencepieceDecodingHandler(spp_nerd_v2)); |
| @@ -657,6 +663,7 @@ router.post('/claude/decode', jsonParser, createWebTokenizerDecodingHandler(clau | ||
| 657 | 663 | router.post('/llama3/decode', jsonParser, createWebTokenizerDecodingHandler(llama3_tokenizer)); |
| 658 | 664 | router.post('/qwen2/decode', jsonParser, createWebTokenizerDecodingHandler(qwen2Tokenizer)); |
| 659 | 665 | router.post('/command-r/decode', jsonParser, createWebTokenizerDecodingHandler(commandTokenizer)); |
| 666 | +router.post('/nemo/decode', jsonParser, createWebTokenizerDecodingHandler(nemoTokenizer)); | |
| 660 | 667 | |
| 661 | 668 | router.post('/openai/encode', jsonParser, async function (req, res) { |
| 662 | 669 | try { |
| @@ -707,6 +714,11 @@ router.post('/openai/encode', jsonParser, async function (req, res) { | ||
| 707 | 714 | return handler(req, res); |
| 708 | 715 | } |
| 709 | 716 | |
| 717 | + if (queryModel.includes('nemo')) { | |
| 718 | + const handler = createWebTokenizerEncodingHandler(nemoTokenizer); | |
| 719 | + return handler(req, res); | |
| 720 | + } | |
| 721 | + | |
| 710 | 722 | const model = getTokenizerModel(queryModel); |
| 711 | 723 | const handler = createTiktokenEncodingHandler(model); |
| 712 | 724 | return handler(req, res); |
| @@ -765,6 +777,11 @@ router.post('/openai/decode', jsonParser, async function (req, res) { | ||
| 765 | 777 | return handler(req, res); |
| 766 | 778 | } |
| 767 | 779 | |
| 780 | + if (queryModel.includes('nemo')) { | |
| 781 | + const handler = createWebTokenizerDecodingHandler(nemoTokenizer); | |
| 782 | + return handler(req, res); | |
| 783 | + } | |
| 784 | + | |
| 768 | 785 | const model = getTokenizerModel(queryModel); |
| 769 | 786 | const handler = createTiktokenDecodingHandler(model); |
| 770 | 787 | return handler(req, res); |
| @@ -835,6 +852,13 @@ router.post('/openai/count', jsonParser, async function (req, res) { | ||
| 835 | 852 | return res.send({ 'token_count': num_tokens }); |
| 836 | 853 | } |
| 837 | 854 | |
| 855 | + if (model === 'nemo') { | |
| 856 | + const instance = await nemoTokenizer.get(); | |
| 857 | + if (!instance) throw new Error('Failed to load the Nemo tokenizer'); | |
| 858 | + num_tokens = countWebTokenizerTokens(instance, req.body); | |
| 859 | + return res.send({ 'token_count': num_tokens }); | |
| 860 | + } | |
| 861 | + | |
| 838 | 862 | const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1; |
| 839 | 863 | const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3; |
| 840 | 864 | const tokensPadding = 3; |
| @@ -4,6 +4,7 @@ const fs = require('fs'); | ||
| 4 | 4 | const express = require('express'); |
| 5 | 5 | const sanitize = require('sanitize-filename'); |
| 6 | 6 | const { jsonParser } = require('../express-common'); |
| 7 | +const { getConfigValue, color } = require('../util'); | |
| 7 | 8 | |
| 8 | 9 | // Don't forget to add new sources to the SOURCES array |
| 9 | 10 | const SOURCES = [ |
| @@ -110,18 +111,94 @@ async function getBatchVector(source, sourceSettings, texts, isQuery, directorie | ||
| 110 | 111 | } |
| 111 | 112 | |
| 112 | 113 | /** |
| 114 | + * Extracts settings for the vectorization sources from the HTTP request headers. | |
| 115 | + * @param {string} source - Which source to extract settings for. | |
| 116 | + * @param {object} request - The HTTP request object. | |
| 117 | + * @returns {object} - An object that can be used as `sourceSettings` in functions that take that parameter. | |
| 118 | + */ | |
| 119 | +function getSourceSettings(source, request) { | |
| 120 | + switch (source) { | |
| 121 | + case 'togetherai': | |
| 122 | + return { | |
| 123 | + model: String(request.headers['x-togetherai-model']), | |
| 124 | + }; | |
| 125 | + case 'openai': | |
| 126 | + return { | |
| 127 | + model: String(request.headers['x-openai-model']), | |
| 128 | + }; | |
| 129 | + case 'cohere': | |
| 130 | + return { | |
| 131 | + model: String(request.headers['x-cohere-model']), | |
| 132 | + }; | |
| 133 | + case 'llamacpp': | |
| 134 | + return { | |
| 135 | + apiUrl: String(request.headers['x-llamacpp-url']), | |
| 136 | + }; | |
| 137 | + case 'vllm': | |
| 138 | + return { | |
| 139 | + apiUrl: String(request.headers['x-vllm-url']), | |
| 140 | + model: String(request.headers['x-vllm-model']), | |
| 141 | + }; | |
| 142 | + case 'ollama': | |
| 143 | + return { | |
| 144 | + apiUrl: String(request.headers['x-ollama-url']), | |
| 145 | + model: String(request.headers['x-ollama-model']), | |
| 146 | + keep: Boolean(request.headers['x-ollama-keep']), | |
| 147 | + }; | |
| 148 | + case 'extras': | |
| 149 | + return { | |
| 150 | + extrasUrl: String(request.headers['x-extras-url']), | |
| 151 | + extrasKey: String(request.headers['x-extras-key']), | |
| 152 | + }; | |
| 153 | + case 'local': | |
| 154 | + return { | |
| 155 | + model: getConfigValue('extras.embeddingModel', ''), | |
| 156 | + }; | |
| 157 | + case 'palm': | |
| 158 | + return { | |
| 159 | + // TODO: Add support for multiple models | |
| 160 | + model: 'text-embedding-004', | |
| 161 | + }; | |
| 162 | + default: | |
| 163 | + return {}; | |
| 164 | + } | |
| 165 | +} | |
| 166 | + | |
| 167 | +/** | |
| 168 | + * Gets the model scope for the source. | |
| 169 | + * @param {object} sourceSettings - The settings for the source | |
| 170 | + * @returns {string} The model scope for the source | |
| 171 | + */ | |
| 172 | +function getModelScope(sourceSettings) { | |
| 173 | + const scopesEnabled = getConfigValue('vectors.enableModelScopes', false); | |
| 174 | + const warningShown = global.process.env.VECTORS_MODEL_SCOPE_WARNING_SHOWN === 'true'; | |
| 175 | + | |
| 176 | + if (!scopesEnabled && !warningShown) { | |
| 177 | + console.log(); | |
| 178 | + console.warn(color.red('[DEPRECATION NOTICE]'), 'Model scopes for Vectore Storage are disabled, but will soon be required.'); | |
| 179 | + console.log(`To enable model scopes, set the ${color.cyan('vectors.enableModelScopes')} in config.yaml to ${color.green(true)}.`); | |
| 180 | + console.log('This message won\'t be shown again in the current session.'); | |
| 181 | + console.log(); | |
| 182 | + global.process.env.VECTORS_MODEL_SCOPE_WARNING_SHOWN = 'true'; | |
| 183 | + } | |
| 184 | + | |
| 185 | + return scopesEnabled ? (sourceSettings?.model || '') : ''; | |
| 186 | +} | |
| 187 | + | |
| 188 | +/** | |
| 113 | 189 | * Gets the index for the vector collection |
| 114 | 190 | * @param {import('../users').UserDirectoryList} directories - User directories |
| 115 | 191 | * @param {string} collectionId - The collection ID |
| 116 | 192 | * @param {string} source - The source of the vector |
| 117 | 193 | * @param {booleanobject} createsourceSettings - WhetherThe tomodel createfor the index if it doesn't existsource |
| 118 | 194 | * @returns {Promise<vectra.LocalIndex>} - The index for the collection |
| 119 | 195 | */ |
| 120 | 196 | async function getIndex(directories, collectionId, source, create = truesourceSettings) { |
| 121 | - const pathToFile = path.join(directories.vectors, sanitize(source), sanitize(collectionId)); | |
| 197 | + const model = getModelScope(sourceSettings); | |
| 198 | + const pathToFile = path.join(directories.vectors, sanitize(source), sanitize(collectionId), sanitize(model)); | |
| 122 | 199 | const store = new vectra.LocalIndex(pathToFile); |
| 123 | 200 | |
| 124 | 201 | if (create && !await store.isIndexCreated()) { |
| 125 | 202 | await store.createIndex(); |
| 126 | 203 | } |
| 127 | 204 | |
| @@ -137,7 +214,7 @@ async function getIndex(directories, collectionId, source, create = true) { | ||
| 137 | 214 | * @param {{ hash: number; text: string; index: number; }[]} items - The items to insert |
| 138 | 215 | */ |
| 139 | 216 | async function insertVectorItems(directories, collectionId, source, sourceSettings, items) { |
| 140 | 217 | const store = await getIndex(directories, collectionId, source, sourceSettings); |
| 141 | 218 | |
| 142 | 219 | await store.beginUpdate(); |
| 143 | 220 | |
| @@ -157,10 +234,11 @@ async function insertVectorItems(directories, collectionId, source, sourceSettin | ||
| 157 | 234 | * @param {import('../users').UserDirectoryList} directories - User directories |
| 158 | 235 | * @param {string} collectionId - The collection ID |
| 159 | 236 | * @param {string} source - The source of the vector |
| 237 | + * @param {Object} sourceSettings - Settings for the source, if it needs any | |
| 160 | 238 | * @returns {Promise<number[]>} - The hashes of the items in the collection |
| 161 | 239 | */ |
| 162 | 240 | async function getSavedHashes(directories, collectionId, source, sourceSettings) { |
| 163 | 241 | const store = await getIndex(directories, collectionId, source, sourceSettings); |
| 164 | 242 | |
| 165 | 243 | const items = await store.listItems(); |
| 166 | 244 | const hashes = items.map(x => Number(x.metadata.hash)); |
| @@ -173,10 +251,11 @@ async function getSavedHashes(directories, collectionId, source) { | ||
| 173 | 251 | * @param {import('../users').UserDirectoryList} directories - User directories |
| 174 | 252 | * @param {string} collectionId - The collection ID |
| 175 | 253 | * @param {string} source - The source of the vector |
| 254 | + * @param {Object} sourceSettings - Settings for the source, if it needs any | |
| 176 | 255 | * @param {number[]} hashes - The hashes of the items to delete |
| 177 | 256 | */ |
| 178 | 257 | async function deleteVectorItems(directories, collectionId, source, sourceSettings, hashes) { |
| 179 | 258 | const store = await getIndex(directories, collectionId, source, sourceSettings); |
| 180 | 259 | const items = await store.listItemsByMetadata({ hash: { '$in': hashes } }); |
| 181 | 260 | |
| 182 | 261 | await store.beginUpdate(); |
| @@ -200,7 +279,7 @@ async function deleteVectorItems(directories, collectionId, source, hashes) { | ||
| 200 | 279 | * @returns {Promise<{hashes: number[], metadata: object[]}>} - The metadata of the items that match the search text |
| 201 | 280 | */ |
| 202 | 281 | async function queryCollection(directories, collectionId, source, sourceSettings, searchText, topK, threshold) { |
| 203 | 282 | const store = await getIndex(directories, collectionId, source, sourceSettings); |
| 204 | 283 | const vector = await getVector(source, sourceSettings, searchText, true, directories); |
| 205 | 284 | |
| 206 | 285 | const result = await store.queryItems(vector, topK); |
| @@ -226,7 +305,7 @@ async function multiQueryCollection(directories, collectionIds, source, sourceSe | ||
| 226 | 305 | const results = []; |
| 227 | 306 | |
| 228 | 307 | for (const collectionId of collectionIds) { |
| 229 | 308 | const store = await getIndex(directories, collectionId, source, sourceSettings); |
| 230 | 309 | const result = await store.queryItems(vector, topK); |
| 231 | 310 | results.push(...result.map(result => ({ collectionId, result }))); |
| 232 | 311 | } |
| @@ -255,71 +334,6 @@ async function multiQueryCollection(directories, collectionIds, source, sourceSe | ||
| 255 | 334 | } |
| 256 | 335 | |
| 257 | 336 | /** |
| 258 | - * Extracts settings for the vectorization sources from the HTTP request headers. | |
| 259 | - * @param {string} source - Which source to extract settings for. | |
| 260 | - * @param {object} request - The HTTP request object. | |
| 261 | - * @returns {object} - An object that can be used as `sourceSettings` in functions that take that parameter. | |
| 262 | - */ | |
| 263 | -function getSourceSettings(source, request) { | |
| 264 | - if (source === 'togetherai') { | |
| 265 | - const model = String(request.headers['x-togetherai-model']); | |
| 266 | - | |
| 267 | - return { | |
| 268 | - model: model, | |
| 269 | - }; | |
| 270 | - } else if (source === 'openai') { | |
| 271 | - const model = String(request.headers['x-openai-model']); | |
| 272 | - | |
| 273 | - return { | |
| 274 | - model: model, | |
| 275 | - }; | |
| 276 | - } else if (source === 'cohere') { | |
| 277 | - const model = String(request.headers['x-cohere-model']); | |
| 278 | - | |
| 279 | - return { | |
| 280 | - model: model, | |
| 281 | - }; | |
| 282 | - } else if (source === 'llamacpp') { | |
| 283 | - const apiUrl = String(request.headers['x-llamacpp-url']); | |
| 284 | - | |
| 285 | - return { | |
| 286 | - apiUrl: apiUrl, | |
| 287 | - }; | |
| 288 | - } else if (source === 'vllm') { | |
| 289 | - const apiUrl = String(request.headers['x-vllm-url']); | |
| 290 | - const model = String(request.headers['x-vllm-model']); | |
| 291 | - | |
| 292 | - return { | |
| 293 | - apiUrl: apiUrl, | |
| 294 | - model: model, | |
| 295 | - }; | |
| 296 | - } else if (source === 'ollama') { | |
| 297 | - const apiUrl = String(request.headers['x-ollama-url']); | |
| 298 | - const model = String(request.headers['x-ollama-model']); | |
| 299 | - const keep = Boolean(request.headers['x-ollama-keep']); | |
| 300 | - | |
| 301 | - return { | |
| 302 | - apiUrl: apiUrl, | |
| 303 | - model: model, | |
| 304 | - keep: keep, | |
| 305 | - }; | |
| 306 | - } else { | |
| 307 | - // Extras API settings to connect to the Extras embeddings provider | |
| 308 | - let extrasUrl = ''; | |
| 309 | - let extrasKey = ''; | |
| 310 | - if (source === 'extras') { | |
| 311 | - extrasUrl = String(request.headers['x-extras-url']); | |
| 312 | - extrasKey = String(request.headers['x-extras-key']); | |
| 313 | - } | |
| 314 | - | |
| 315 | - return { | |
| 316 | - extrasUrl: extrasUrl, | |
| 317 | - extrasKey: extrasKey, | |
| 318 | - }; | |
| 319 | - } | |
| 320 | -} | |
| 321 | - | |
| 322 | -/** | |
| 323 | 337 | * Performs a request to regenerate the index if it is corrupted. |
| 324 | 338 | * @param {import('express').Request} req Express request object |
| 325 | 339 | * @param {import('express').Response} res Express response object |
| @@ -330,9 +344,10 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) { | ||
| 330 | 344 | if (error instanceof SyntaxError && !req.query.regenerated) { |
| 331 | 345 | const collectionId = String(req.body.collectionId); |
| 332 | 346 | const source = String(req.body.source) || 'transformers'; |
| 347 | + const sourceSettings = getSourceSettings(source, req); | |
| 333 | 348 | |
| 334 | 349 | if (collectionId && source) { |
| 335 | 350 | const index = await getIndex(req.user.directories, collectionId, source, falsesourceSettings); |
| 336 | 351 | const exists = await index.isIndexCreated(); |
| 337 | 352 | |
| 338 | 353 | if (exists) { |
| @@ -350,6 +365,11 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) { | ||
| 350 | 365 | |
| 351 | 366 | const router = express.Router(); |
| 352 | 367 | |
| 368 | +router.get('/scopes-enabled', (_req, res) => { | |
| 369 | + const scopesEnabled = getConfigValue('vectors.enableModelScopes', false); | |
| 370 | + return res.json({ enabled: scopesEnabled }); | |
| 371 | +}); | |
| 372 | + | |
| 353 | 373 | router.post('/query', jsonParser, async (req, res) => { |
| 354 | 374 | try { |
| 355 | 375 | if (!req.body.collectionId || !req.body.searchText) { |
| @@ -416,8 +436,9 @@ router.post('/list', jsonParser, async (req, res) => { | ||
| 416 | 436 | |
| 417 | 437 | const collectionId = String(req.body.collectionId); |
| 418 | 438 | const source = String(req.body.source) || 'transformers'; |
| 439 | + const sourceSettings = getSourceSettings(source, req); | |
| 419 | 440 | |
| 420 | 441 | const hashes = await getSavedHashes(req.user.directories, collectionId, source, sourceSettings); |
| 421 | 442 | return res.json(hashes); |
| 422 | 443 | } catch (error) { |
| 423 | 444 | return regenerateCorruptedIndexErrorHandler(req, res, error); |
| @@ -433,8 +454,9 @@ router.post('/delete', jsonParser, async (req, res) => { | ||
| 433 | 454 | const collectionId = String(req.body.collectionId); |
| 434 | 455 | const hashes = req.body.hashes.map(x => Number(x)); |
| 435 | 456 | const source = String(req.body.source) || 'transformers'; |
| 457 | + const sourceSettings = getSourceSettings(source, req); | |
| 436 | 458 | |
| 437 | 459 | await deleteVectorItems(req.user.directories, collectionId, source, sourceSettings, hashes); |
| 438 | 460 | return res.sendStatus(200); |
| 439 | 461 | } catch (error) { |
| 440 | 462 | return regenerateCorruptedIndexErrorHandler(req, res, error); |
| @@ -468,17 +490,12 @@ router.post('/purge', jsonParser, async (req, res) => { | ||
| 468 | 490 | const collectionId = String(req.body.collectionId); |
| 469 | 491 | |
| 470 | 492 | for (const source of SOURCES) { |
| 471 | 493 | const indexsourcePath = await getIndexpath.join(req.user.directories, collectionId.vectors, sanitize(source), falsesanitize(collectionId)); |
| 472 | - | |
| 494 | + if (!fs.existsSync(sourcePath)) { | |
| 473 | - const exists = await index.isIndexCreated(); | |
| 474 | - | |
| 475 | - if (!exists) { | |
| 476 | 495 | continue; |
| 477 | 496 | } |
| 478 | - | |
| 497 | + await fs.promises.rm(sourcePath, { recursive: true }); | |
| 479 | - const path = index.folderPath; | |
| 498 | + console.log(`Deleted vector index at ${sourcePath}`); | |
| 480 | - await index.deleteIndex(); | |
| 481 | - console.log(`Deleted vector index at ${path}`); | |
| 482 | 499 | } |
| 483 | 500 | |
| 484 | 501 | return res.sendStatus(200); |
| @@ -267,11 +267,14 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = ' | ||
| 267 | 267 | 'gemini-1.5-flash', |
| 268 | 268 | 'gemini-1.5-flash-latest', |
| 269 | 269 | 'gemini-1.5-flash-001', |
| 270 | + 'gemini-1.5-flash-002', | |
| 270 | 271 | 'gemini-1.5-flash-exp-0827', |
| 271 | 272 | 'gemini-1.5-flash-8b-exp-0827', |
| 273 | + 'gemini-1.5-flash-8b-exp-0924', | |
| 272 | 274 | 'gemini-1.5-pro', |
| 273 | 275 | 'gemini-1.5-pro-latest', |
| 274 | 276 | 'gemini-1.5-pro-001', |
| 277 | + 'gemini-1.5-pro-002', | |
| 275 | 278 | 'gemini-1.5-pro-exp-0801', |
| 276 | 279 | 'gemini-1.5-pro-exp-0827', |
| 277 | 280 | 'gemini-1.0-pro-vision-latest', |
| @@ -9,8 +9,10 @@ const storage = require('node-persist'); | ||
| 9 | 9 | const express = require('express'); |
| 10 | 10 | const mime = require('mime-types'); |
| 11 | 11 | const archiver = require('archiver'); |
| 12 | +const writeFileAtomicSync = require('write-file-atomic').sync; | |
| 13 | +const _ = require('lodash'); | |
| 12 | 14 | |
| 13 | 15 | const { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, DEFAULT_AVATAR, SETTINGS_FILE } = require('./constants'); |
| 14 | 16 | const { getConfigValue, color, delay, setConfigValue, generateTimestamp } = require('./util'); |
| 15 | 17 | const { readSecret, writeSecret } = require('./endpoints/secrets'); |
| 16 | 18 | |
| @@ -24,6 +26,7 @@ const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64'); | ||
| 24 | 26 | * @type {Map<string, UserDirectoryList>} |
| 25 | 27 | */ |
| 26 | 28 | const DIRECTORIES_CACHE = new Map(); |
| 29 | +const PUBLIC_USER_AVATAR = '/img/default-user.png'; | |
| 27 | 30 | |
| 28 | 31 | const STORAGE_KEYS = { |
| 29 | 32 | csrfSecret: 'csrfSecret', |
| @@ -82,6 +85,7 @@ const STORAGE_KEYS = { | ||
| 82 | 85 | * @property {string} files - The directory where the uploaded files are stored |
| 83 | 86 | * @property {string} vectors - The directory where the vectors are stored |
| 84 | 87 | * @property {string} backups - The directory where the backups are stored |
| 88 | + * @property {string} sysprompt - The directory where the system prompt data is stored | |
| 85 | 89 | */ |
| 86 | 90 | |
| 87 | 91 | /** |
| @@ -322,6 +326,60 @@ async function migrateUserData() { | ||
| 322 | 326 | console.log(color.green('Migration completed!')); |
| 323 | 327 | } |
| 324 | 328 | |
| 329 | +async function migrateSystemPrompts() { | |
| 330 | + /** | |
| 331 | + * Gets the default system prompts. | |
| 332 | + * @returns {Promise<any[]>} - The list of default system prompts | |
| 333 | + */ | |
| 334 | + async function getDefaultSystemPrompts() { | |
| 335 | + try { | |
| 336 | + const { getContentOfType } = await import('./endpoints/content-manager.js'); | |
| 337 | + return getContentOfType('sysprompt', 'json'); | |
| 338 | + } catch { | |
| 339 | + return []; | |
| 340 | + } | |
| 341 | + } | |
| 342 | + | |
| 343 | + const directories = await getUserDirectoriesList(); | |
| 344 | + for (const directory of directories) { | |
| 345 | + try { | |
| 346 | + const migrateMarker = path.join(directory.sysprompt, '.migrated'); | |
| 347 | + if (fs.existsSync(migrateMarker)) { | |
| 348 | + continue; | |
| 349 | + } | |
| 350 | + const defaultPrompts = await getDefaultSystemPrompts(); | |
| 351 | + const instucts = fs.readdirSync(directory.instruct); | |
| 352 | + let migratedPrompts = []; | |
| 353 | + for (const instruct of instucts) { | |
| 354 | + const instructPath = path.join(directory.instruct, instruct); | |
| 355 | + const sysPromptPath = path.join(directory.sysprompt, instruct); | |
| 356 | + if (path.extname(instruct) === '.json' && !fs.existsSync(sysPromptPath)) { | |
| 357 | + const instructData = JSON.parse(fs.readFileSync(instructPath, 'utf8')); | |
| 358 | + if ('system_prompt' in instructData && 'name' in instructData) { | |
| 359 | + const syspromptData = { name: instructData.name, content: instructData.system_prompt }; | |
| 360 | + migratedPrompts.push(syspromptData); | |
| 361 | + delete instructData.system_prompt; | |
| 362 | + writeFileAtomicSync(instructPath, JSON.stringify(instructData, null, 4)); | |
| 363 | + } | |
| 364 | + } | |
| 365 | + } | |
| 366 | + // Only leave unique contents | |
| 367 | + migratedPrompts = _.uniqBy(migratedPrompts, 'content'); | |
| 368 | + // Only leave contents that are not in the default prompts | |
| 369 | + migratedPrompts = migratedPrompts.filter(x => !defaultPrompts.some(y => y.content === x.content)); | |
| 370 | + for (const sysPromptData of migratedPrompts) { | |
| 371 | + sysPromptData.name = `[Migrated] ${sysPromptData.name}`; | |
| 372 | + const syspromptPath = path.join(directory.sysprompt, `${sysPromptData.name}.json`); | |
| 373 | + writeFileAtomicSync(syspromptPath, JSON.stringify(sysPromptData, null, 4)); | |
| 374 | + console.log(`Migrated system prompt ${sysPromptData.name} for ${directory.root.split(path.sep).pop()}`); | |
| 375 | + } | |
| 376 | + writeFileAtomicSync(migrateMarker, ''); | |
| 377 | + } catch { | |
| 378 | + // Ignore errors | |
| 379 | + } | |
| 380 | + } | |
| 381 | +} | |
| 382 | + | |
| 325 | 383 | /** |
| 326 | 384 | * Converts a user handle to a storage key. |
| 327 | 385 | * @param {string} handle User handle |
| @@ -478,11 +536,11 @@ async function getUserAvatar(handle) { | ||
| 478 | 536 | const settings = fs.existsSync(pathToSettings) ? JSON.parse(fs.readFileSync(pathToSettings, 'utf8')) : {}; |
| 479 | 537 | const avatarFile = settings?.power_user?.default_persona || settings?.user_avatar; |
| 480 | 538 | if (!avatarFile) { |
| 481 | 539 | return DEFAULT_AVATARPUBLIC_USER_AVATAR; |
| 482 | 540 | } |
| 483 | 541 | const avatarPath = path.join(directory.avatars, avatarFile); |
| 484 | 542 | if (!fs.existsSync(avatarPath)) { |
| 485 | 543 | return DEFAULT_AVATARPUBLIC_USER_AVATAR; |
| 486 | 544 | } |
| 487 | 545 | const mimeType = mime.lookup(avatarPath); |
| 488 | 546 | const base64Content = fs.readFileSync(avatarPath, 'base64'); |
| @@ -490,7 +548,7 @@ async function getUserAvatar(handle) { | ||
| 490 | 548 | } |
| 491 | 549 | catch { |
| 492 | 550 | // Ignore errors |
| 493 | 551 | return DEFAULT_AVATARPUBLIC_USER_AVATAR; |
| 494 | 552 | } |
| 495 | 553 | } |
| 496 | 554 | |
| @@ -723,6 +781,7 @@ module.exports = { | ||
| 723 | 781 | requireLoginMiddleware, |
| 724 | 782 | requireAdminMiddleware, |
| 725 | 783 | migrateUserData, |
| 784 | + migrateSystemPrompts, | |
| 726 | 785 | getPasswordSalt, |
| 727 | 786 | getPasswordHash, |
| 728 | 787 | getCsrfSecret, |
| @@ -26,7 +26,7 @@ fi | ||
| 26 | 26 | |
| 27 | 27 | echo "Installing Node Modules..." |
| 28 | 28 | export NODE_ENV=production |
| 29 | 29 | npm i --no-audit --no-fund --quietloglevel=error --no-progress --omit=dev |
| 30 | 30 | |
| 31 | 31 | echo "Entering SillyTavern..." |
| 32 | 32 | node "server.js" "$@" |