From cd8dd5fc8a7e8a6421570128b6f0a7fe98a54a57 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:31:34 +0300 Subject: [PATCH 001/268] XTC for ooba --- public/index.html | 3 ++- public/scripts/textgen-settings.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 4403372bf..eb0bcee4a 100644 --- a/public/index.html +++ b/public/index.html @@ -1279,7 +1279,7 @@ -
+

@@ -1649,6 +1649,7 @@
Top A
Min P
Mirostat
+
XTC

-
- -
- - If both Instruct Mode and this are enabled, the prompt will be formatted by SillyTavern using the current - advanced formatting settings (except instruct System Prompt). If disabled, the prompt will be formatted by OpenRouter. - -
-
+ + + To use instruct formatting, switch to OpenRouter under Text Completion API. +
diff --git a/public/locales/ar-sa.json b/public/locales/ar-sa.json index f599ccd2b..90ef674cf 100644 --- a/public/locales/ar-sa.json +++ b/public/locales/ar-sa.json @@ -381,10 +381,6 @@ "Group by vendors Description": "ضع نماذج OpenAI في مجموعة واحدة، والنماذج الإنسانية في مجموعة أخرى، وما إلى ذلك. ويمكن دمجها مع الفرز.", "Allow fallback routes": "السماح بمسارات الاحتياط", "Allow fallback routes Description": "يختار النموذج البديل تلقائيًا إذا كان النموذج المحدد غير قادر على تلبية طلبك.", - "openrouter_force_instruct": "هذا الخيار قديم وسيتم إزالته في المستقبل. لاستخدام تنسيق التعليمات، يرجى التبديل إلى OpenRouter ضمن Text Completion API بدلاً من ذلك.", - "LEGACY": "إرث", - "Force Instruct Mode formatting": "فرض تنسيق وضع التعليمات", - "Force_Instruct_Mode_formatting_Description": "إذا تم تمكين وضع التعليمات وهذا، فسيتم تنسيق المطالبة بواسطة SillyTavern باستخدام التيار\n إعدادات التنسيق المتقدمة (باستثناء توجيه موجه النظام). إذا تم تعطيله، فسيتم تنسيق المطالبة بواسطة OpenRouter.", "Scale API Key": "مفتاح API لـ Scale", "Clear your cookie": "امسح ملف تعريف الارتباط الخاص بك", "Alt Method": "طريقة بديلة", diff --git a/public/locales/de-de.json b/public/locales/de-de.json index 4d37d5806..ac4e63298 100644 --- a/public/locales/de-de.json +++ b/public/locales/de-de.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Platzieren Sie OpenAI-Modelle in einer Gruppe, anthropogene Modelle in einer anderen Gruppe usw. Kann mit Sortierung kombiniert werden.", "Allow fallback routes": "Fallback-Routen zulassen", "Allow fallback routes Description": "Das alternative Modell wird automatisch ausgewählt, wenn das ausgewählte Modell Ihre Anfrage nicht erfüllen kann.", - "openrouter_force_instruct": "Diese Option ist veraltet und wird in Zukunft entfernt. Um die Formatierung mit Anweisungen zu verwenden, wechseln Sie stattdessen zu OpenRouter unter Text Completion API.", - "LEGACY": "VERMÄCHTNIS", - "Force Instruct Mode formatting": "Formatierung im Force Instruct Mode", - "Force_Instruct_Mode_formatting_Description": "Wenn sowohl der Anweisungsmodus als auch dieser aktiviert sind, wird die Eingabeaufforderung von SillyTavern mit den aktuellen erweiterten Formatierungseinstellungen formatiert (außer „Anweisungssystem-Eingabeaufforderung“). Wenn deaktiviert, wird die Eingabeaufforderung von OpenRouter formatiert.", "Scale API Key": "Scale API-Schlüssel", "Clear your cookie": "Löschen Sie Ihre Cookies", "Alt Method": "Alternative Methode", diff --git a/public/locales/es-es.json b/public/locales/es-es.json index 8db9a8cc4..b7b746e5d 100644 --- a/public/locales/es-es.json +++ b/public/locales/es-es.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Coloque los modelos OpenAI en un grupo, los modelos antrópicos en otro grupo, etc. Se puede combinar con la clasificación.", "Allow fallback routes": "Permitir rutas de respaldo", "Allow fallback routes Description": "El modelo alternativo se elige automáticamente si el modelo seleccionado no puede cumplir con tu solicitud.", - "openrouter_force_instruct": "Esta opción está desactualizada y se eliminará en el futuro. Para utilizar el formato de instrucciones, cambie a OpenRouter en API de finalización de texto.", - "LEGACY": "LEGADO", - "Force Instruct Mode formatting": "Forzar formato en modo de instrucción", - "Force_Instruct_Mode_formatting_Description": "Si tanto el modo de instrucción como este están habilitados, SillyTavern formateará el mensaje usando el formato actual.\n configuraciones de formato avanzadas (excepto instrucciones del mensaje del sistema). Si está deshabilitado, OpenRouter formateará el mensaje.", "Scale API Key": "Clave API de Scale", "Clear your cookie": "Limpia tu cookie", "Alt Method": "Método alternativo", diff --git a/public/locales/fr-fr.json b/public/locales/fr-fr.json index 2ae937879..e069895b6 100644 --- a/public/locales/fr-fr.json +++ b/public/locales/fr-fr.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Placez les modèles OpenAI dans un groupe, les modèles Anthropic dans un autre groupe, etc. Peut être combiné avec le tri.", "Allow fallback routes": "Autoriser les itinéraires de secours", "Allow fallback routes Description": "Le modèle alternatif est automatiquement sélectionné si le modèle choisi ne peut pas répondre à votre demande.", - "openrouter_force_instruct": "Cette option est obsolète et sera supprimée à l'avenir. Pour utiliser le formatage des instructions, veuillez plutôt passer à OpenRouter sous API de saisie semi-automatique de texte.", - "LEGACY": "HÉRITAGE", - "Force Instruct Mode formatting": "Forcer le formatage du mode instruction", - "Force_Instruct_Mode_formatting_Description": "Si le mode Instruct et celui-ci sont activés, l'invite sera formatée par SillyTavern en utilisant le mode actuel.\n paramètres de formatage avancés (à l'exception de l'invite système). Si elle est désactivée, l'invite sera formatée par OpenRouter.", "Scale API Key": "Clé API Scale", "Clear your cookie": "Effacer vos cookies", "Alt Method": "Méthode alternative", diff --git a/public/locales/is-is.json b/public/locales/is-is.json index f03d6c533..634a7c2c2 100644 --- a/public/locales/is-is.json +++ b/public/locales/is-is.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Setjið OpenAI módel í einn hóp, Anthropic módel í annan hóp osfrv. Hægt að sameina við flokkun.", "Allow fallback routes": "Leyfa bakfallssvæði", "Allow fallback routes Description": "Veldur hlutleysa vélbúnaðarinn við val þinn ef valið módel getur ekki uppfyllt beiðni þína.", - "openrouter_force_instruct": "Þessi valkostur er úreltur og verður fjarlægður í framtíðinni. Til að nota leiðbeiningarsnið skaltu skipta yfir í OpenRouter undir Text Completion API í staðinn.", - "LEGACY": "ARFIÐ", - "Force Instruct Mode formatting": "Force Instruct Mode formatting", - "Force_Instruct_Mode_formatting_Description": "Ef bæði leiðbeiningarhamur og þessi eru virkjuð, verður kvaðningurinn sniðinn af SillyTavern með því að nota núverandi\n háþróaðar sniðstillingar (nema leiðbeiningar um System Prompt). Ef slökkt er á henni verður hvetjan sniðin af OpenRouter.", "Scale API Key": "Lykill API fyrir Scale", "Clear your cookie": "Hreinsaðu kökuna þína", "Alt Method": "Aðferð Bakmenn", diff --git a/public/locales/it-it.json b/public/locales/it-it.json index 10fc75d2b..bab851988 100644 --- a/public/locales/it-it.json +++ b/public/locales/it-it.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Metti i modelli OpenAI in un gruppo, i modelli antropici in un altro gruppo, ecc. Può essere combinato con l'ordinamento.", "Allow fallback routes": "Consenti percorsi alternativi", "Allow fallback routes Description": "Il modello alternativo viene automaticamente scelto se il modello selezionato non può soddisfare la tua richiesta.", - "openrouter_force_instruct": "Questa opzione è obsoleta e verrà rimossa in futuro. Per usare la formattazione instruct, passa a OpenRouter in Text Completion API.", - "LEGACY": "EREDITÀ", - "Force Instruct Mode formatting": "Forza la formattazione della modalità istruzione", - "Force_Instruct_Mode_formatting_Description": "Se sia la modalità Instruct che questa sono abilitate, il prompt verrà formattato da SillyTavern utilizzando il file current\n impostazioni di formattazione avanzate (ad eccezione del prompt di sistema). Se disabilitato, il prompt verrà formattato da OpenRouter.", "Scale API Key": "Chiave API di Scale", "Clear your cookie": "Cancella il tuo cookie", "Alt Method": "Metodo alternativo", diff --git a/public/locales/ja-jp.json b/public/locales/ja-jp.json index 474f32a99..88453ad0c 100644 --- a/public/locales/ja-jp.json +++ b/public/locales/ja-jp.json @@ -381,10 +381,6 @@ "Group by vendors Description": "OpenAI モデルを 1 つのグループに、Anthropic モデルを別のグループに配置するなどします。ソートと組み合わせることができます。", "Allow fallback routes": "フォールバックルートを許可", "Allow fallback routes Description": "選択したモデルが要求を満たせない場合、代替モデルが自動的に選択されます。", - "openrouter_force_instruct": "このオプションは古く、将来削除される予定です。指示されたフォーマットを使用するには、代わりにテキスト補完 API の OpenRouter に切り替えてください。", - "LEGACY": "遺産", - "Force Instruct Mode formatting": "強制指示モードのフォーマット", - "Force_Instruct_Mode_formatting_Description": "Instruct Mode とこれが両方とも有効になっている場合、プロンプトは SillyTavern によって現在の高度なフォーマット設定 (instruct System Prompt を除く) を使用してフォーマットされます。無効になっている場合、プロンプトは OpenRouter によってフォーマットされます。", "Scale API Key": "ScaleのAPIキー", "Clear your cookie": "クッキーを消去する", "Alt Method": "代替手法", diff --git a/public/locales/ko-kr.json b/public/locales/ko-kr.json index 1ba7985af..919209f92 100644 --- a/public/locales/ko-kr.json +++ b/public/locales/ko-kr.json @@ -381,10 +381,6 @@ "Group by vendors Description": "OpenAI 모델을 한 그룹에 넣고, Anthropic 모델을 다른 그룹에 두는 등 정렬을 통해 결합할 수 있습니다.", "Allow fallback routes": "대체 경로 허용", "Allow fallback routes Description": "선택한 모델이 요청을 처리할 수 없는 경우 대체 모델이 자동으로 선택됩니다.", - "openrouter_force_instruct": "이 옵션은 오래되었으며 향후 제거될 예정입니다. 지시 형식을 사용하려면 대신 Text Completion API에서 OpenRouter로 전환하세요.", - "LEGACY": "유산", - "Force Instruct Mode formatting": "강제 지시 모드 포맷", - "Force_Instruct_Mode_formatting_Description": "Instruct Mode와 이 모드가 모두 활성화된 경우 프롬프트는 SillyTavern에 의해 현재 형식을 사용하여 형식화됩니다.\n 고급 형식 설정(시스템 프롬프트 지시 제외) 비활성화된 경우 프롬프트는 OpenRouter에 의해 형식화됩니다.", "Scale API Key": "Scale API 키", "Clear your cookie": "쿠키 지우기", "Alt Method": "대체 방법", diff --git a/public/locales/nl-nl.json b/public/locales/nl-nl.json index 069c89b9c..a0e7e8f9b 100644 --- a/public/locales/nl-nl.json +++ b/public/locales/nl-nl.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Plaats OpenAI-modellen in één groep, antropische modellen in een andere groep, enz. Kan worden gecombineerd met sorteren.", "Allow fallback routes": "Fallback-routes toestaan", "Allow fallback routes Description": "Het alternatieve model wordt automatisch gekozen als het geselecteerde model niet aan uw verzoek kan voldoen.", - "openrouter_force_instruct": "Deze optie is verouderd en zal in de toekomst worden verwijderd. Om instructie-opmaak te gebruiken, schakelt u in plaats daarvan over naar OpenRouter onder Text Completion API.", - "LEGACY": "NALATENSCHAP", - "Force Instruct Mode formatting": "Forceer de opmaak van de instructiemodus", - "Force_Instruct_Mode_formatting_Description": "Als zowel de Instruct-modus als deze zijn ingeschakeld, wordt de prompt door SillyTavern geformatteerd met behulp van de current\n geavanceerde opmaakinstellingen (behalve de opdracht Systeemprompt). Indien uitgeschakeld, wordt de prompt geformatteerd door OpenRouter.", "Scale API Key": "Scale API-sleutel", "Clear your cookie": "Wis uw cookie", "Alt Method": "Alternatieve methode", diff --git a/public/locales/pt-pt.json b/public/locales/pt-pt.json index 771d60a94..1ecfd5222 100644 --- a/public/locales/pt-pt.json +++ b/public/locales/pt-pt.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Coloque os modelos OpenAI em um grupo, os modelos antrópicos em outro grupo, etc.", "Allow fallback routes": "Permitir rotas de fallback", "Allow fallback routes Description": "O modelo alternativo será escolhido automaticamente se o modelo selecionado não puder atender à sua solicitação.", - "openrouter_force_instruct": "Esta opção está desatualizada e será removida no futuro. Para usar a formatação de instruções, mude para OpenRouter em Text Completion API.", - "LEGACY": "LEGADO", - "Force Instruct Mode formatting": "Forçar formatação do modo de instrução", - "Force_Instruct_Mode_formatting_Description": "Se o Modo Instruir e este estiverem habilitados, o prompt será formatado pelo SillyTavern usando o atual\n configurações avançadas de formatação (exceto instruir o prompt do sistema). Se desativado, o prompt será formatado pelo OpenRouter.", "Scale API Key": "Chave da API Scale", "Clear your cookie": "Limpe seu cookie", "Alt Method": "Método Alternativo", diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index d7ce65466..22aac03d3 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -732,8 +732,6 @@ "Context Size": "По размеру контекста", "Group by vendors": "Сгруппировать по владельцу", "Group by vendors Description": "Модели от OpenAI попадут в одну группу, от Anthropic - в другую, и т.д. Можно комбинировать с сортировкой.", - "LEGACY": "УСТАР.", - "Force Instruct Mode formatting": "Включить форматирование для Instruct-режима", "Allow Jailbreak": "Разрешить джейлбрейк", "System Prompt Wrapping": "Обрамление для системного промпта", "System Prompt Prefix": "Префикс системного промпта", @@ -1270,8 +1268,6 @@ "vLLM Model": "Модель vLLM", "Aphrodite Model": "Модель Aphrodite", "Peek a password": "Посмотреть пароль", - "openrouter_force_instruct": "This option is outdated and will be removed in the future. To use instruct formatting, please switch to OpenRouter under Text Completion API instead.", - "Force_Instruct_Mode_formatting_Description": "If both Instruct Mode and this are enabled, the prompt will be formatted by SillyTavern using the current\n advanced formatting settings (except instruct System Prompt). If disabled, the prompt will be formatted by OpenRouter.", "Clear your cookie": "Clear your cookie", "Add Chat Start and Example Separator to a list of stopping strings.": "Использовать Начало чата и Разделитель примеров сообщений в качестве стоп-строк.", "context_allow_jailbreak": "Если в карточке есть джейлбрейк И ПРИ ЭТОМ включена опция \"Приоритет джейлбрейку из карточки персонажа\", то этот джейлбрейк добавляется в конец промпта.\nНЕ РЕКОМЕНДУЕТСЯ ДЛЯ МОДЕЛЕЙ TEXT COMPLETION, МОЖЕТ ПОРТИТЬ ВЫХОДНОЙ ТЕКСТ.", diff --git a/public/locales/uk-ua.json b/public/locales/uk-ua.json index 3a6d9c4e2..7fa6e2ea3 100644 --- a/public/locales/uk-ua.json +++ b/public/locales/uk-ua.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Помістіть моделі OpenAI в одну групу, моделі Anthropic в іншу групу тощо. Можна поєднати з сортуванням.", "Allow fallback routes": "Дозволити резервні маршрути", "Allow fallback routes Description": "Автоматично вибирає альтернативну модель, якщо вибрана модель не може задовольнити ваш запит.", - "openrouter_force_instruct": "Цей параметр застарів і буде видалено в майбутньому. Щоб використовувати форматування інструкцій, перейдіть натомість до OpenRouter у розділі Text Completion API.", - "LEGACY": "СПАДОК", - "Force Instruct Mode formatting": "Примусове форматування в режимі вказівок", - "Force_Instruct_Mode_formatting_Description": "Якщо ввімкнути обидва режими Instruction і цей режим, підказка буде відформатована SillyTavern за допомогою поточного\n розширені параметри форматування (крім системного запиту). Якщо вимкнено, запит буде відформатовано OpenRouter.", "Scale API Key": "Ключ API для Scale", "Clear your cookie": "Очистіть файл cookie", "Alt Method": "Альтернативний метод", diff --git a/public/locales/vi-vn.json b/public/locales/vi-vn.json index c1d6ca7f8..268fdce77 100644 --- a/public/locales/vi-vn.json +++ b/public/locales/vi-vn.json @@ -381,10 +381,6 @@ "Group by vendors Description": "Xếp các mô hình OpenAI vào một nhóm, các mô hình Anthropic vào một nhóm khác, v.v. Có thể kết hợp với việc sắp xếp.", "Allow fallback routes": "Cho phép các tuyến đường phụ", "Allow fallback routes Description": "Bot thay thế tự động nếu mô hình được chọn không thể đáp ứng yêu cầu của bạn.", - "openrouter_force_instruct": "Tùy chọn này đã lỗi thời và sẽ bị xóa trong tương lai. Để sử dụng định dạng hướng dẫn, vui lòng chuyển sang OpenRouter trong API hoàn thành văn bản.", - "LEGACY": "Cũ", - "Force Instruct Mode formatting": "Buộc định dạng Instruct Mode", - "Force_Instruct_Mode_formatting_Description": "Nếu cả Instruct Mode và chế độ này được bật, Prompt sẽ được SillyTavern định dạng bằng cách sử dụng\n cài đặt định dạng nâng cao (ngoại trừ hướng dẫn System Nhắc). Nếu bị tắt, Prompt sẽ được OpenRouter định dạng.", "Scale API Key": "Scale API Key", "Clear your cookie": "Xóa cookie", "Alt Method": "Phương pháp thay thế", diff --git a/public/locales/zh-cn.json b/public/locales/zh-cn.json index 7fc78e51a..df8b7f831 100644 --- a/public/locales/zh-cn.json +++ b/public/locales/zh-cn.json @@ -397,10 +397,6 @@ "Context Size": "上下文大小", "Group by vendors": "按供应商分组", "Group by vendors Description": "将 OpenAI 模型放在一组,将 Anthropic 模型放在另一组,等等。可以与排序结合。", - "openrouter_force_instruct": "此选项已过时,将来会被删除。要使用指令格式,请改用文本完成 API 下的 OpenRouter。", - "LEGACY": "旧版", - "Force Instruct Mode formatting": "强制指令模式格式化", - "Force_Instruct_Mode_formatting_Description": "如果同时启用了“指示模式”和“系统提示词”,则 SillyTavern 将使用当前\n高级格式设置(指示系统提示词除外)对提示词进行格式化。如果禁用,则 OpenRouter 将对提示词进行格式化。", "Scale API Key": "Scale API密钥", "Clear your cookie": "清除你的 Cookie", "Alt Method": "备用方法", diff --git a/public/locales/zh-tw.json b/public/locales/zh-tw.json index c3b8869cc..59270882f 100644 --- a/public/locales/zh-tw.json +++ b/public/locales/zh-tw.json @@ -382,10 +382,6 @@ "Group by vendors Description": "將 OpenAI 、 Anthropic 等等的模型放各自供應商的群組中。可以與排序功能結合使用。", "Allow fallback routes": "允許備援路徑", "Allow fallback routes Description": "如果選擇的模型無法滿足要求,會自動選擇替代模型。", - "openrouter_force_instruct": "這個選項已經過時,將來會被移除。如果要使用指令格式,請改在 Text Completion API 中選擇 OpenRouter。", - "LEGACY": "遺留", - "Force Instruct Mode formatting": "強制指示模式格式化", - "Force_Instruct_Mode_formatting_Description": "如果同時啟用「指令模式」和這個選項,\nSillyTavern 會根據目前的進階格式化設定(不包括指令系統提示)來格式化提示詞。\n如果停用這個選項,提示詞將由 OpenRouter 來進行格式化。", "Scale API Key": "Scale API 金鑰", "Clear your cookie": "清除您的 Cookie", "Alt Method": "替代方法", diff --git a/public/script.js b/public/script.js index a5ad50717..64eb81826 100644 --- a/public/script.js +++ b/public/script.js @@ -96,7 +96,6 @@ import { openai_messages_count, chat_completion_sources, getChatCompletionModel, - isOpenRouterWithInstruct, proxies, loadProxyPresets, selected_proxy, @@ -3927,8 +3926,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro if (isContinue) { // Coping mechanism for OAI spacing - const isForceInstruct = isOpenRouterWithInstruct(); - if (main_api === 'openai' && !isForceInstruct && !cyclePrompt.endsWith(' ')) { + if (main_api === 'openai' && !cyclePrompt.endsWith(' ')) { cyclePrompt += oai_settings.continue_postfix; continue_mag += oai_settings.continue_postfix; } diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 0b86d8f5e..ceb670dff 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -61,12 +61,6 @@ import { stringFormat, } from './utils.js'; import { countTokensOpenAI, getTokenizerModel } from './tokenizers.js'; -import { - formatInstructModeChat, - formatInstructModeExamples, - formatInstructModePrompt, - formatInstructModeSystemPrompt, -} from './instruct-mode.js'; import { isMobile } from './RossAscends-mods.js'; import { saveLogprobsForActiveMessage } from './logprobs.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; @@ -263,7 +257,6 @@ const default_settings = { windowai_model: '', openrouter_model: openrouter_website_model, openrouter_use_fallback: false, - openrouter_force_instruct: false, openrouter_group_models: false, openrouter_sort_models: 'alphabetically', openrouter_providers: [], @@ -341,7 +334,6 @@ const oai_settings = { windowai_model: '', openrouter_model: openrouter_website_model, openrouter_use_fallback: false, - openrouter_force_instruct: false, openrouter_group_models: false, openrouter_sort_models: 'alphabetically', openrouter_providers: [], @@ -416,108 +408,6 @@ async function validateReverseProxy() { localStorage.setItem(rememberKey, String(true)); } -/** - * Converts the Chat Completion object to an Instruct Mode prompt string. - * @param {object[]} messages Array of messages - * @param {string} type Generation type - * @returns {string} Text completion prompt - */ -function convertChatCompletionToInstruct(messages, type) { - const newChatPrompts = [ - substituteParams(oai_settings.new_chat_prompt), - substituteParams(oai_settings.new_example_chat_prompt), - substituteParams(oai_settings.new_group_chat_prompt), - ]; - messages = messages.filter(x => !newChatPrompts.includes(x.content)); - - let chatMessagesText = ''; - let systemPromptText = ''; - let examplesText = ''; - - function getPrefix(message) { - let prefix; - - if (message.role === 'user' || message.name === 'example_user') { - if (selected_group) { - prefix = ''; - } else if (message.name === 'example_user') { - prefix = name1; - } else { - prefix = message.name ?? name1; - } - } - - if (message.role === 'assistant' || message.name === 'example_assistant') { - if (selected_group) { - prefix = ''; - } - else if (message.name === 'example_assistant') { - prefix = name2; - } else { - prefix = message.name ?? name2; - } - } - - return prefix; - } - - function toString(message) { - if (message.role === 'system' && !message.name) { - return message.content; - } - - const prefix = getPrefix(message); - return prefix ? `${prefix}: ${message.content}` : message.content; - } - - const firstChatMessage = messages.findIndex(message => message.role === 'assistant' || message.role === 'user'); - const systemPromptMessages = messages.slice(0, firstChatMessage).filter(message => message.role === 'system' && !message.name); - - if (systemPromptMessages.length) { - systemPromptText = systemPromptMessages.map(message => message.content).join('\n'); - systemPromptText = formatInstructModeSystemPrompt(systemPromptText); - } - - const exampleMessages = messages.filter(x => x.role === 'system' && (x.name === 'example_user' || x.name === 'example_assistant')); - - if (exampleMessages.length) { - const blockHeading = power_user.context.example_separator ? (substituteParams(power_user.context.example_separator) + '\n') : ''; - const examplesArray = exampleMessages.map(m => '\n' + toString(m)); - examplesText = blockHeading + formatInstructModeExamples(examplesArray, name1, name2).join(''); - } - - const chatMessages = messages.slice(firstChatMessage); - - if (chatMessages.length) { - chatMessagesText = substituteParams(power_user.context.chat_start) + '\n'; - - for (const message of chatMessages) { - const name = getPrefix(message); - const isUser = message.role === 'user'; - const isNarrator = message.role === 'system'; - chatMessagesText += formatInstructModeChat(name, message.content, isUser, isNarrator, '', name1, name2, false); - } - } - - const isImpersonate = type === 'impersonate'; - const isContinue = type === 'continue'; - const isQuiet = type === 'quiet'; - const isQuietToLoud = false; // Quiet to loud not implemented for Chat Completion - const promptName = isImpersonate ? name1 : name2; - const promptLine = isContinue ? '' : formatInstructModePrompt(promptName, isImpersonate, '', name1, name2, isQuiet, isQuietToLoud).trimStart(); - - let prompt = [systemPromptText, examplesText, chatMessagesText, promptLine] - .filter(x => x) - .map(x => x.endsWith('\n') ? x : `${x}\n`) - .join(''); - - if (isContinue) { - prompt = prompt.replace(/\n$/, ''); - } - - return prompt; -} - /** * Formats chat messages into chat completion messages. * @param {object[]} chat - Array containing all messages. @@ -761,10 +651,6 @@ function populationInjectionPrompts(prompts, messages) { return messages; } -export function isOpenRouterWithInstruct() { - return oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER && oai_settings.openrouter_force_instruct && power_user.instruct.enabled; -} - /** * Populates the chat history of the conversation. * @param {object[]} messages - Array containing all messages. @@ -795,8 +681,7 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul // Reserve budget for continue nudge let continueMessage = null; - const instruct = isOpenRouterWithInstruct(); - if (type === 'continue' && cyclePrompt && !instruct && !oai_settings.continue_prefill) { + if (type === 'continue' && cyclePrompt && !oai_settings.continue_prefill) { const promptObject = { identifier: 'continueNudge', role: 'system', @@ -1793,7 +1678,7 @@ async function sendOpenAIRequest(type, messages, signal) { const isPerplexity = oai_settings.chat_completion_source == chat_completion_sources.PERPLEXITY; const isGroq = oai_settings.chat_completion_source == chat_completion_sources.GROQ; const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; - const isTextCompletion = (isOAI && textCompletionModels.includes(oai_settings.openai_model)) || (isOpenRouter && oai_settings.openrouter_force_instruct && power_user.instruct.enabled); + const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); const isQuiet = type === 'quiet'; const isImpersonate = type === 'impersonate'; const isContinue = type === 'continue'; @@ -1801,11 +1686,6 @@ async function sendOpenAIRequest(type, messages, signal) { const useLogprobs = !!power_user.request_token_probabilities; const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom); - if (isTextCompletion && isOpenRouter) { - messages = convertChatCompletionToInstruct(messages, type); - replaceItemizedPromptText(messageId, messages); - } - // If we're using the window.ai extension, use that instead // Doesn't support logit bias yet if (oai_settings.chat_completion_source == chat_completion_sources.WINDOWAI) { @@ -3045,7 +2925,6 @@ function loadOpenAISettings(data, settings) { oai_settings.openrouter_group_models = settings.openrouter_group_models ?? default_settings.openrouter_group_models; oai_settings.openrouter_sort_models = settings.openrouter_sort_models ?? default_settings.openrouter_sort_models; oai_settings.openrouter_use_fallback = settings.openrouter_use_fallback ?? default_settings.openrouter_use_fallback; - oai_settings.openrouter_force_instruct = settings.openrouter_force_instruct ?? default_settings.openrouter_force_instruct; oai_settings.openrouter_allow_fallbacks = settings.openrouter_allow_fallbacks ?? default_settings.openrouter_allow_fallbacks; oai_settings.ai21_model = settings.ai21_model ?? default_settings.ai21_model; oai_settings.mistralai_model = settings.mistralai_model ?? default_settings.mistralai_model; @@ -3152,7 +3031,6 @@ function loadOpenAISettings(data, settings) { $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt); $('#scale-alt').prop('checked', oai_settings.use_alt_scale); $('#openrouter_use_fallback').prop('checked', oai_settings.openrouter_use_fallback); - $('#openrouter_force_instruct').prop('checked', oai_settings.openrouter_force_instruct); $('#openrouter_group_models').prop('checked', oai_settings.openrouter_group_models); $('#openrouter_allow_fallbacks').prop('checked', oai_settings.openrouter_allow_fallbacks); $('#openrouter_providers_chat').val(oai_settings.openrouter_providers).trigger('change'); @@ -3380,7 +3258,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) { windowai_model: settings.windowai_model, openrouter_model: settings.openrouter_model, openrouter_use_fallback: settings.openrouter_use_fallback, - openrouter_force_instruct: settings.openrouter_force_instruct, openrouter_group_models: settings.openrouter_group_models, openrouter_sort_models: settings.openrouter_sort_models, openrouter_providers: settings.openrouter_providers, @@ -3817,7 +3694,6 @@ function onSettingsPresetChange() { windowai_model: ['#model_windowai_select', 'windowai_model', false], openrouter_model: ['#model_openrouter_select', 'openrouter_model', false], openrouter_use_fallback: ['#openrouter_use_fallback', 'openrouter_use_fallback', true], - openrouter_force_instruct: ['#openrouter_force_instruct', 'openrouter_force_instruct', true], openrouter_group_models: ['#openrouter_group_models', 'openrouter_group_models', false], openrouter_sort_models: ['#openrouter_sort_models', 'openrouter_sort_models', false], openrouter_providers: ['#openrouter_providers_chat', 'openrouter_providers', false], @@ -4780,7 +4656,7 @@ export function isImageInliningSupported() { case chat_completion_sources.CLAUDE: return visionSupportedModels.some(model => oai_settings.claude_model.includes(model)); case chat_completion_sources.OPENROUTER: - return !oai_settings.openrouter_force_instruct; + return true; case chat_completion_sources.CUSTOM: return true; case chat_completion_sources.ZEROONEAI: @@ -5202,11 +5078,6 @@ $(document).ready(async function () { saveSettingsDebounced(); }); - $('#openrouter_force_instruct').on('input', function () { - oai_settings.openrouter_force_instruct = !!$(this).prop('checked'); - saveSettingsDebounced(); - }); - $('#openrouter_group_models').on('input', function () { oai_settings.openrouter_group_models = !!$(this).prop('checked'); saveSettingsDebounced(); From a1af768b02e467403432bb69da86470071515a5a Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 22 Sep 2024 02:32:43 +0200 Subject: [PATCH 009/268] Refactor /if to fromProps --- public/scripts/variables.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 3e9028591..47383f038 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -1274,8 +1274,12 @@ export function registerVariableCommands() { enumProvider: commonEnumProviders.variables('all'), forceEnum: false, }), - new SlashCommandNamedArgument( - 'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [ + SlashCommandNamedArgument.fromProps({ + name: 'rule', + description: 'comparison rule', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumList: [ new SlashCommandEnumValue('gt', 'a > b'), new SlashCommandEnumValue('gte', 'a >= b'), new SlashCommandEnumValue('lt', 'a < b'), @@ -1286,10 +1290,13 @@ export function registerVariableCommands() { new SlashCommandEnumValue('in', 'a includes b'), new SlashCommandEnumValue('nin', 'a not includes b'), ], - ), - new SlashCommandNamedArgument( - 'else', 'command to execute if not true', [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], false, - ), + }), + SlashCommandNamedArgument.fromProps({ + name: 'else', + description: 'command to execute if not true', + typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], + isRequired: false, + }), ], unnamedArgumentList: [ new SlashCommandArgument( From aea95adf60b088bfff5e7776a023e1c171ef9144 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 22 Sep 2024 07:42:51 +0200 Subject: [PATCH 010/268] /if allow "rule" and "right" to be optional --- public/scripts/variables.js | 50 +++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 47383f038..3bee981df 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -11,7 +11,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; -import { isFalseBoolean, convertValueType } from './utils.js'; +import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; /** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */ /** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */ @@ -463,7 +463,7 @@ function existsGlobalVariable(name) { /** * Parses boolean operands from command arguments. * @param {object} args Command arguments - * @returns {{a: string | number, b: string | number, rule: string}} Boolean operands + * @returns {{a: string | number, b: string | number?, rule: string}} Boolean operands */ export function parseBooleanOperands(args) { // Resolution order: numeric literal, local variable, global variable, string literal @@ -472,6 +472,9 @@ export function parseBooleanOperands(args) { */ function getOperand(operand) { if (operand === undefined) { + return undefined; + } + if (operand === '') { return ''; } @@ -500,9 +503,9 @@ export function parseBooleanOperands(args) { return stringLiteral || ''; } - const left = getOperand(args.a || args.left || args.first || args.x); - const right = getOperand(args.b || args.right || args.second || args.y); - const rule = args.rule; + const left = getOperand(args.a ?? args.left ?? args.first ?? args.x); + const right = getOperand(args.b ?? args.right ?? args.second ?? args.y); + const rule = args.rule ?? 'eq'; return { a: left, b: right, rule }; } @@ -511,16 +514,22 @@ export function parseBooleanOperands(args) { * Evaluates a boolean comparison rule. * @param {string} rule Boolean comparison rule * @param {string|number} a The left operand - * @param {string|number} b The right operand + * @param {string|number?} b The right operand * @returns {boolean} True if the rule yields true, false otherwise */ export function evalBoolean(rule, a, b) { - if (!rule) { - toastr.warning('The rule must be specified for the boolean comparison.', 'Invalid command'); - throw new Error('Invalid command.'); + let result = false; + + if (b === undefined && rule === 'eq') { + // If right-hand side was not provided, whe just check if the left side is truthy + if (isTrueBoolean(String(a))) return true; + if (isFalseBoolean(String(a))) return false; + return !!a; } - let result = false; + // Restore old behavior, where b cannot be undefined + b = b ?? ''; + if (typeof a === 'number' && typeof b === 'number') { // only do numeric comparison if both operands are numbers const aNumber = Number(a); @@ -582,7 +591,7 @@ export function evalBoolean(rule, a, b) { break; default: toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command'); - throw new Error('Invalid command.'); + throw new Error('Unknown boolean comparison rule for type string.'); } } @@ -1264,21 +1273,18 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], isRequired: true, enumProvider: commonEnumProviders.variables('all'), - forceEnum: false, }), SlashCommandNamedArgument.fromProps({ name: 'right', description: 'right operand', typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], - isRequired: true, enumProvider: commonEnumProviders.variables('all'), - forceEnum: false, }), SlashCommandNamedArgument.fromProps({ name: 'rule', description: 'comparison rule', typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, + defaultValue: 'eq', enumList: [ new SlashCommandEnumValue('gt', 'a > b'), new SlashCommandEnumValue('gte', 'a >= b'), @@ -1290,12 +1296,12 @@ export function registerVariableCommands() { new SlashCommandEnumValue('in', 'a includes b'), new SlashCommandEnumValue('nin', 'a not includes b'), ], + forceEnum: true, }), SlashCommandNamedArgument.fromProps({ name: 'else', description: 'command to execute if not true', typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], - isRequired: false, }), ], unnamedArgumentList: [ @@ -1313,6 +1319,11 @@ export function registerVariableCommands() {
Numeric values and string literals for left and right operands supported.
+
+ If the rule is not provided, it defaults to eq.
+ If no right operand is provided, it defaults to checking the left value to be truthy. + A non-empty string or non-zero number is considered truthy, as is the value true. +
Available rules:
    @@ -1334,6 +1345,13 @@ export function registerVariableCommands() {
    /if left=score right=10 rule=gte "/speak You win"
    triggers a /speak command if the value of "score" is greater or equals 10. +
  • +
    /if left={{lastMessage}} rule=in right=surprise {: /echo SURPISE! :}
    + executes a subcommand defined as a closure if the given value contains a specified word. +
  • +
    /if left=myContent {: /echo "My content had some content." :}
    + executes the defined subcommand, if the provided value of left is truthy (contains some kind of contant that is not empty or false) +
`, From cfe08d3a53d191a31fe7ebd893433c439413f67e Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 22 Sep 2024 07:58:16 +0200 Subject: [PATCH 011/268] Refactor evalBoolean code --- public/scripts/variables.js | 51 ++++++++++--------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 3bee981df..32d65554f 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -518,8 +518,6 @@ export function parseBooleanOperands(args) { * @returns {boolean} True if the rule yields true, false otherwise */ export function evalBoolean(rule, a, b) { - let result = false; - if (b === undefined && rule === 'eq') { // If right-hand side was not provided, whe just check if the left side is truthy if (isTrueBoolean(String(a))) return true; @@ -537,65 +535,42 @@ export function evalBoolean(rule, a, b) { switch (rule) { case 'not': - result = !aNumber; - break; + return !aNumber; case 'gt': - result = aNumber > bNumber; - break; + return aNumber > bNumber; case 'gte': - result = aNumber >= bNumber; - break; + return aNumber >= bNumber; case 'lt': - result = aNumber < bNumber; - break; + return aNumber < bNumber; case 'lte': - result = aNumber <= bNumber; - break; + return aNumber <= bNumber; case 'eq': - result = aNumber === bNumber; - break; + return aNumber === bNumber; case 'neq': - result = aNumber !== bNumber; - break; + return aNumber !== bNumber; default: toastr.error('Unknown boolean comparison rule for type number.', 'Invalid command'); throw new Error('Invalid command.'); } } else { // otherwise do case-insensitive string comparsion, stringify non-strings - let aString; - let bString; - if (typeof a == 'string') { - aString = a.toLowerCase(); - } else { - aString = JSON.stringify(a).toLowerCase(); - } - if (typeof b == 'string') { - bString = b.toLowerCase(); - } else { - bString = JSON.stringify(b).toLowerCase(); - } + let aString = (typeof a === 'string') ? a.toLowerCase() : JSON.stringify(a).toLowerCase(); + let bString = (typeof b === 'string') ? b.toLowerCase() : JSON.stringify(b).toLowerCase(); switch (rule) { case 'in': - result = aString.includes(bString); - break; + return aString.includes(bString); case 'nin': - result = !aString.includes(bString); - break; + return !aString.includes(bString); case 'eq': - result = aString === bString; - break; + return aString === bString; case 'neq': - result = aString !== bString; - break; + return aString !== bString; default: toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command'); throw new Error('Unknown boolean comparison rule for type string.'); } } - - return result; } /** From 8ae7c2330a18952be82fac7c3bf26f803ae67754 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 22 Sep 2024 08:09:19 +0200 Subject: [PATCH 012/268] Adjust /while to fit new optional args --- public/scripts/variables.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 32d65554f..19ccfe26b 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -1342,18 +1342,19 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], isRequired: true, enumProvider: commonEnumProviders.variables('all'), - forceEnum: false, }), SlashCommandNamedArgument.fromProps({ name: 'right', description: 'right operand', typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], - isRequired: true, enumProvider: commonEnumProviders.variables('all'), - forceEnum: false, }), - new SlashCommandNamedArgument( - 'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [ + SlashCommandNamedArgument.fromProps({ + name: 'rule', + description: 'comparison rule', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'eq', + enumList: [ new SlashCommandEnumValue('gt', 'a > b'), new SlashCommandEnumValue('gte', 'a >= b'), new SlashCommandEnumValue('lt', 'a < b'), @@ -1364,10 +1365,15 @@ export function registerVariableCommands() { new SlashCommandEnumValue('in', 'a includes b'), new SlashCommandEnumValue('nin', 'a not includes b'), ], - ), - new SlashCommandNamedArgument( - 'guard', 'disable loop iteration limit', [ARGUMENT_TYPE.STRING], false, false, null, commonEnumProviders.boolean('onOff')(), - ), + forceEnum: true, + }), + SlashCommandNamedArgument.fromProps({ + name: 'guard', + description: 'disable loop iteration limit', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'off', + enumList: commonEnumProviders.boolean('onOff')(), + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -1404,6 +1410,10 @@ export function registerVariableCommands() {
/setvar key=i 0 | /while left=i right=10 rule=lte "/addvar key=i 1"
adds 1 to the value of "i" until it reaches 10. +
  • +
    /while left={{getvar::currentword}} {: /setvar key=currentword {: /do-something-and-return :}() | /echo The current work is "{{getvar::currentword}}" :}
    + executes the defined subcommand as long as the "currentword" variable is truthy (has any content that is not false/empty) +
  • From 2ee3eb700433e6d3020be78df1e0f2a3518692b5 Mon Sep 17 00:00:00 2001 From: M0cho <77959408+M0ch0@users.noreply.github.com> Date: Wed, 25 Sep 2024 02:58:56 +0900 Subject: [PATCH 013/268] Support gemini-1.5-series-002 and new 8B exp model --- public/index.html | 3 +++ public/scripts/openai.js | 5 +++++ src/prompt-converters.js | 3 +++ 3 files changed, 11 insertions(+) diff --git a/public/index.html b/public/index.html index 79bff508c..09f08b569 100644 --- a/public/index.html +++ b/public/index.html @@ -2894,10 +2894,13 @@ + + + diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 057ceaa0e..6c906a9cf 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -4128,6 +4128,8 @@ async function onModelChange() { $('#openai_max_context').attr('max', max_2mil); } else if (value.includes('gemini-1.5-pro')) { $('#openai_max_context').attr('max', max_2mil); + } else if (value.match('gemini-1.5-flash-002')) { + $('#openai_max_context').attr('max', max_2mil); } else if (value.includes('gemini-1.5-flash')) { $('#openai_max_context').attr('max', max_1mil); } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') { @@ -4774,12 +4776,15 @@ export function isImageInliningSupported() { 'gemini-1.5-flash', 'gemini-1.5-flash-latest', 'gemini-1.5-flash-001', + 'gemini-1.5-flash-002', 'gemini-1.5-flash-exp-0827', 'gemini-1.5-flash-8b-exp-0827', + 'gemini-1.5-flash-8b-exp-0924', 'gemini-1.0-pro-vision-latest', 'gemini-1.5-pro', 'gemini-1.5-pro-latest', 'gemini-1.5-pro-001', + 'gemini-1.5-pro-002', 'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0827', 'gemini-pro-vision', diff --git a/src/prompt-converters.js b/src/prompt-converters.js index fd687b5d1..5d52adb6e 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -267,11 +267,14 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = ' 'gemini-1.5-flash', 'gemini-1.5-flash-latest', 'gemini-1.5-flash-001', + 'gemini-1.5-flash-002', 'gemini-1.5-flash-exp-0827', 'gemini-1.5-flash-8b-exp-0827', + 'gemini-1.5-flash-8b-exp-0924', 'gemini-1.5-pro', 'gemini-1.5-pro-latest', 'gemini-1.5-pro-001', + 'gemini-1.5-pro-002', 'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0827', 'gemini-1.0-pro-vision-latest', From df3d7a048edf0d3fb2e45e47d0581ae01726f2ee Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:51:10 +0300 Subject: [PATCH 014/268] Deprecate unscoped vectors --- default/config.yaml | 3 -- public/scripts/extensions/vectors/index.js | 26 ---------------- .../scripts/extensions/vectors/settings.html | 8 ----- src/endpoints/vectors.js | 31 +++++++------------ 4 files changed, 11 insertions(+), 57 deletions(-) diff --git a/default/config.yaml b/default/config.yaml index 8a55f6afb..585988686 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -110,9 +110,6 @@ enableExtensionsAutoUpdate: true # Additional model tokenizers can be downloaded on demand. # Disabling will fallback to another locally available tokenizer. enableDownloadableTokenizers: true -# Vector storage settings -vectors: - enableModelScopes: false # Extension settings extras: # Disables automatic model download from HuggingFace diff --git a/public/scripts/extensions/vectors/index.js b/public/scripts/extensions/vectors/index.js index 408af9cf1..3d1a7a539 100644 --- a/public/scripts/extensions/vectors/index.js +++ b/public/scripts/extensions/vectors/index.js @@ -999,25 +999,6 @@ async function purgeAllVectorIndexes() { } } -async function isModelScopesEnabled() { - try { - const response = await fetch('/api/vector/scopes-enabled', { - method: 'GET', - headers: getVectorHeaders(), - }); - - if (!response.ok) { - return false; - } - - const data = await response.json(); - return data?.enabled ?? false; - } catch (error) { - console.error('Vectors: Failed to check model scopes', error); - return false; - } -} - function toggleSettings() { $('#vectors_files_settings').toggle(!!settings.enabled_files); $('#vectors_chats_settings').toggle(!!settings.enabled_chats); @@ -1282,7 +1263,6 @@ jQuery(async () => { } Object.assign(settings, extension_settings.vectors); - const scopesEnabled = await isModelScopesEnabled(); // Migrate from TensorFlow to Transformers settings.source = settings.source !== 'local' ? settings.source : 'transformers'; @@ -1294,7 +1274,6 @@ jQuery(async () => { saveSettingsDebounced(); toggleSettings(); }); - $('#vectors_modelWarning').hide(); $('#vectors_enabled_files').prop('checked', settings.enabled_files).on('input', () => { settings.enabled_files = $('#vectors_enabled_files').prop('checked'); Object.assign(extension_settings.vectors, settings); @@ -1334,31 +1313,26 @@ jQuery(async () => { saveSettingsDebounced(); }); $('#vectors_togetherai_model').val(settings.togetherai_model).on('change', () => { - !scopesEnabled && $('#vectors_modelWarning').show(); settings.togetherai_model = String($('#vectors_togetherai_model').val()); Object.assign(extension_settings.vectors, settings); saveSettingsDebounced(); }); $('#vectors_openai_model').val(settings.openai_model).on('change', () => { - !scopesEnabled && $('#vectors_modelWarning').show(); settings.openai_model = String($('#vectors_openai_model').val()); Object.assign(extension_settings.vectors, settings); saveSettingsDebounced(); }); $('#vectors_cohere_model').val(settings.cohere_model).on('change', () => { - !scopesEnabled && $('#vectors_modelWarning').show(); settings.cohere_model = String($('#vectors_cohere_model').val()); Object.assign(extension_settings.vectors, settings); saveSettingsDebounced(); }); $('#vectors_ollama_model').val(settings.ollama_model).on('input', () => { - !scopesEnabled && $('#vectors_modelWarning').show(); settings.ollama_model = String($('#vectors_ollama_model').val()); Object.assign(extension_settings.vectors, settings); saveSettingsDebounced(); }); $('#vectors_vllm_model').val(settings.vllm_model).on('input', () => { - !scopesEnabled && $('#vectors_modelWarning').show(); settings.vllm_model = String($('#vectors_vllm_model').val()); Object.assign(extension_settings.vectors, settings); saveSettingsDebounced(); diff --git a/public/scripts/extensions/vectors/settings.html b/public/scripts/extensions/vectors/settings.html index f1e73016e..a7686a55d 100644 --- a/public/scripts/extensions/vectors/settings.html +++ b/public/scripts/extensions/vectors/settings.html @@ -96,14 +96,6 @@
    - - - - Set vectors.enableModelScopes to true in config.yaml to switch between vectorization models without needing to purge existing vectors. - This option will soon be enabled by default. - - -
    From cd6e0747a53191d32196934831fc1583bc464645 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:35:28 +0000 Subject: [PATCH 025/268] Fix margin on Hint --- .../scripts/extensions/connection-manager/profile.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/scripts/extensions/connection-manager/profile.html b/public/scripts/extensions/connection-manager/profile.html index bc2f9d352..3611b85dd 100644 --- a/public/scripts/extensions/connection-manager/profile.html +++ b/public/scripts/extensions/connection-manager/profile.html @@ -10,10 +10,12 @@ {{/each}}
    - - Hint: - Click on the setting name to omit it from the profile. - +
    + + Hint: + Click on the setting name to omit it from the profile. + +

    Enter a name:

    From 17e279addfe4195d9c03b31453770a3fcee56d4f Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:58:07 +0000 Subject: [PATCH 026/268] Fix sysprompts set with commands --- public/scripts/sysprompt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/sysprompt.js b/public/scripts/sysprompt.js index 5b1ef89bb..6d54f0220 100644 --- a/public/scripts/sysprompt.js +++ b/public/scripts/sysprompt.js @@ -143,7 +143,7 @@ function selectSystemPromptCallback(args, name) { foundName = result[0].item; } - $select.val(foundName).trigger('input'); + $select.val(foundName).trigger('change'); !quiet && toastr.success(`System prompt "${foundName}" selected`); return foundName; } From fbc590b6417500bfc626cd058f081006e97ef489 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:02:45 +0000 Subject: [PATCH 027/268] Tabby: Unhide UI controls for XTC --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index de3f399e4..9e53da59f 100644 --- a/public/index.html +++ b/public/index.html @@ -1310,7 +1310,7 @@ -
    +

    From a11a8fe95624271d16ea879f916332fb45dc727f Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 19:40:13 +0200 Subject: [PATCH 028/268] Update eval logic to be more streamlined --- public/scripts/variables.js | 64 ++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 19ccfe26b..abe69799e 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -505,28 +505,36 @@ export function parseBooleanOperands(args) { const left = getOperand(args.a ?? args.left ?? args.first ?? args.x); const right = getOperand(args.b ?? args.right ?? args.second ?? args.y); - const rule = args.rule ?? 'eq'; + const rule = args.rule; return { a: left, b: right, rule }; } /** * Evaluates a boolean comparison rule. - * @param {string} rule Boolean comparison rule + * + * @param {string?} rule Boolean comparison rule * @param {string|number} a The left operand * @param {string|number?} b The right operand * @returns {boolean} True if the rule yields true, false otherwise */ export function evalBoolean(rule, a, b) { - if (b === undefined && rule === 'eq') { - // If right-hand side was not provided, whe just check if the left side is truthy - if (isTrueBoolean(String(a))) return true; - if (isFalseBoolean(String(a))) return false; - return !!a; + // If right-hand side was not provided, whe just check if the left side is truthy + if (b === undefined) { + switch (rule) { + case undefined: + case 'not': + const resultOnTruthy = rule !== 'not'; + if (isTrueBoolean(String(a))) return resultOnTruthy; + if (isFalseBoolean(String(a))) return !resultOnTruthy; + return !!a ? resultOnTruthy : !resultOnTruthy; + default: + throw new Error(`Unknown boolean comparison rule for truthy check. If right-hand side is not provided, the rule must not provided or be "not". Provided: ${rule}`); + } } - // Restore old behavior, where b cannot be undefined - b = b ?? ''; + // If no rule was provided, we are implicitly using 'eq', as defined for the slash commands + rule ??= 'eq'; if (typeof a === 'number' && typeof b === 'number') { // only do numeric comparison if both operands are numbers @@ -534,8 +542,6 @@ export function evalBoolean(rule, a, b) { const bNumber = Number(b); switch (rule) { - case 'not': - return !aNumber; case 'gt': return aNumber > bNumber; case 'gte': @@ -549,27 +555,25 @@ export function evalBoolean(rule, a, b) { case 'neq': return aNumber !== bNumber; default: - toastr.error('Unknown boolean comparison rule for type number.', 'Invalid command'); - throw new Error('Invalid command.'); + throw new Error(`Unknown boolean comparison rule for type number. Accepted: gt, gte, lt, lte, eq, neq. Provided: ${rule}`); } - } else { - // otherwise do case-insensitive string comparsion, stringify non-strings - let aString = (typeof a === 'string') ? a.toLowerCase() : JSON.stringify(a).toLowerCase(); - let bString = (typeof b === 'string') ? b.toLowerCase() : JSON.stringify(b).toLowerCase(); + } - switch (rule) { - case 'in': - return aString.includes(bString); - case 'nin': - return !aString.includes(bString); - case 'eq': - return aString === bString; - case 'neq': - return aString !== bString; - default: - toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command'); - throw new Error('Unknown boolean comparison rule for type string.'); - } + // otherwise do case-insensitive string comparsion, stringify non-strings + let aString = (typeof a === 'string') ? a.toLowerCase() : JSON.stringify(a).toLowerCase(); + let bString = (typeof b === 'string') ? b.toLowerCase() : JSON.stringify(b).toLowerCase(); + + switch (rule) { + case 'in': + return aString.includes(bString); + case 'nin': + return !aString.includes(bString); + case 'eq': + return aString === bString; + case 'neq': + return aString !== bString; + default: + throw new Error(`Unknown boolean comparison rule for type number. Accepted: in, nin, eq, neq. Provided: ${rule}`); } } From dadfc4db988018d256e3d92ed9e1cfb999ad9f6a Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 20:10:14 +0200 Subject: [PATCH 029/268] Let in/nin fall through to string + docs update --- public/scripts/variables.js | 92 +++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index abe69799e..9edc0940b 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -529,7 +529,7 @@ export function evalBoolean(rule, a, b) { if (isFalseBoolean(String(a))) return !resultOnTruthy; return !!a ? resultOnTruthy : !resultOnTruthy; default: - throw new Error(`Unknown boolean comparison rule for truthy check. If right-hand side is not provided, the rule must not provided or be "not". Provided: ${rule}`); + throw new Error(`Unknown boolean comparison rule for truthy check. If right operand is not provided, the rule must not provided or be 'not'. Provided: ${rule}`); } } @@ -554,6 +554,11 @@ export function evalBoolean(rule, a, b) { return aNumber === bNumber; case 'neq': return aNumber !== bNumber; + case 'in': + case 'nin': + // Fall through to string comparison. Otherwise you could not check if 12345 contains 45 for example. + console.debug(`Boolean comparison rule '${rule}' is not supported for type number. Falling back to string comparison.`); + break; default: throw new Error(`Unknown boolean comparison rule for type number. Accepted: gt, gte, lt, lte, eq, neq. Provided: ${rule}`); } @@ -1265,15 +1270,15 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.STRING], defaultValue: 'eq', enumList: [ - new SlashCommandEnumValue('gt', 'a > b'), - new SlashCommandEnumValue('gte', 'a >= b'), - new SlashCommandEnumValue('lt', 'a < b'), - new SlashCommandEnumValue('lte', 'a <= b'), - new SlashCommandEnumValue('eq', 'a == b'), - new SlashCommandEnumValue('neq', 'a !== b'), - new SlashCommandEnumValue('not', '!a'), - new SlashCommandEnumValue('in', 'a includes b'), - new SlashCommandEnumValue('nin', 'a not includes b'), + new SlashCommandEnumValue('eq', 'a == b (strings & numbers)'), + new SlashCommandEnumValue('neq', 'a !== b (strings & numbers)'), + new SlashCommandEnumValue('in', 'a includes b (strings & numbers as strings)'), + new SlashCommandEnumValue('nin', 'a not includes b (strings & numbers as strings)'), + new SlashCommandEnumValue('gt', 'a > b (numbers)'), + new SlashCommandEnumValue('gte', 'a >= b (numbers)'), + new SlashCommandEnumValue('lt', 'a < b (numbers)'), + new SlashCommandEnumValue('lte', 'a <= b (numbers)'), + new SlashCommandEnumValue('not', '!a (truthy)'), ], forceEnum: true, }), @@ -1299,22 +1304,25 @@ export function registerVariableCommands() { Numeric values and string literals for left and right operands supported.

    - If the rule is not provided, it defaults to eq.
    + If the rule is not provided, it defaults to eq. +
    +
    If no right operand is provided, it defaults to checking the left value to be truthy. - A non-empty string or non-zero number is considered truthy, as is the value true. + A non-empty string or non-zero number is considered truthy, as is the value true or on.
    + Only acceptable rules for no provided right operand are not, and no provided rule - which default to returning whether it is not or is truthy.
    Available rules:
      -
    • gt => a > b
    • -
    • gte => a >= b
    • -
    • lt => a < b
    • -
    • lte => a <= b
    • -
    • eq => a == b
    • -
    • neq => a != b
    • -
    • not => !a
    • -
    • in (strings) => a includes b
    • -
    • nin (strings) => a not includes b
    • +
    • eq => a == b (strings & numbers)
    • +
    • neq => a !== b (strings & numbers)
    • +
    • in => a includes b (strings & numbers as strings)
    • +
    • nin => a not includes b (strings & numbers as strings)
    • +
    • gt => a > b (numbers)
    • +
    • gte => a >= b (numbers)
    • +
    • lt => a < b (numbers)
    • +
    • lte => a <= b (numbers)
    • +
    • not => !a (truthy)
    @@ -1328,9 +1336,13 @@ export function registerVariableCommands() {
    /if left={{lastMessage}} rule=in right=surprise {: /echo SURPISE! :}
    executes a subcommand defined as a closure if the given value contains a specified word.
  • -
    /if left=myContent {: /echo "My content had some content." :}
    +
    /if left=myContent {: /echo My content had some content. :}
    executes the defined subcommand, if the provided value of left is truthy (contains some kind of contant that is not empty or false)
  • +
  • +
    /if left=tree right={{getvar::object}} {: /echo The object is a tree! :}
    + executes the defined subcommand, if the left and right values are equals. +
  • `, @@ -1359,15 +1371,15 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.STRING], defaultValue: 'eq', enumList: [ - new SlashCommandEnumValue('gt', 'a > b'), - new SlashCommandEnumValue('gte', 'a >= b'), - new SlashCommandEnumValue('lt', 'a < b'), - new SlashCommandEnumValue('lte', 'a <= b'), - new SlashCommandEnumValue('eq', 'a == b'), - new SlashCommandEnumValue('neq', 'a !== b'), - new SlashCommandEnumValue('not', '!a'), - new SlashCommandEnumValue('in', 'a includes b'), - new SlashCommandEnumValue('nin', 'a not includes b'), + new SlashCommandEnumValue('eq', 'a == b (strings & numbers)'), + new SlashCommandEnumValue('neq', 'a !== b (strings & numbers)'), + new SlashCommandEnumValue('in', 'a includes b (strings & numbers as strings)'), + new SlashCommandEnumValue('nin', 'a not includes b (strings & numbers as strings)'), + new SlashCommandEnumValue('gt', 'a > b (numbers)'), + new SlashCommandEnumValue('gte', 'a >= b (numbers)'), + new SlashCommandEnumValue('lt', 'a < b (numbers)'), + new SlashCommandEnumValue('lte', 'a <= b (numbers)'), + new SlashCommandEnumValue('not', '!a (truthy)'), ], forceEnum: true, }), @@ -1396,15 +1408,15 @@ export function registerVariableCommands() {
    Available rules:
      -
    • gt => a > b
    • -
    • gte => a >= b
    • -
    • lt => a < b
    • -
    • lte => a <= b
    • -
    • eq => a == b
    • -
    • neq => a != b
    • -
    • not => !a
    • -
    • in (strings) => a includes b
    • -
    • nin (strings) => a not includes b
    • +
    • eq => a == b (strings & numbers)
    • +
    • neq => a !== b (strings & numbers)
    • +
    • in => a includes b (strings & numbers as strings)
    • +
    • nin => a not includes b (strings & numbers as strings)
    • +
    • gt => a > b (numbers)
    • +
    • gte => a >= b (numbers)
    • +
    • lt => a < b (numbers)
    • +
    • lte => a <= b (numbers)
    • +
    • not => !a (truthy)
    From 23286d186bc40355b4cebb74935ca8f5fbd7bc73 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 20:44:28 +0200 Subject: [PATCH 030/268] Fix lint issues --- public/scripts/variables.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 9edc0940b..443bbbd30 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -523,11 +523,12 @@ export function evalBoolean(rule, a, b) { if (b === undefined) { switch (rule) { case undefined: - case 'not': + case 'not': { const resultOnTruthy = rule !== 'not'; if (isTrueBoolean(String(a))) return resultOnTruthy; if (isFalseBoolean(String(a))) return !resultOnTruthy; - return !!a ? resultOnTruthy : !resultOnTruthy; + return a ? resultOnTruthy : !resultOnTruthy; + } default: throw new Error(`Unknown boolean comparison rule for truthy check. If right operand is not provided, the rule must not provided or be 'not'. Provided: ${rule}`); } From 8c87a24e5da71543202272478747080d28a16b24 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 20:51:56 +0200 Subject: [PATCH 031/268] Throw when left operand not provided --- public/scripts/variables.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 81e6afc8c..e9cd8c653 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -544,6 +544,10 @@ export function parseBooleanOperands(args) { * @returns {boolean} True if the rule yields true, false otherwise */ export function evalBoolean(rule, a, b) { + if (a === undefined) { + throw new Error('Left operand is not provided'); + } + // If right-hand side was not provided, whe just check if the left side is truthy if (b === undefined) { switch (rule) { From ff989b3352b085f04063ed8e5a267e3bc2599e0e Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 21:58:46 +0200 Subject: [PATCH 032/268] Move extensions init to function --- public/script.js | 3 ++- public/scripts/extensions.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/script.js b/public/script.js index 8bfbe5a64..cb750e915 100644 --- a/public/script.js +++ b/public/script.js @@ -158,7 +158,7 @@ import { } from './scripts/utils.js'; import { debounce_timeout } from './scripts/constants.js'; -import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js'; +import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, initExtensions, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js'; import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, registerSlashCommand, stopScriptExecution } from './scripts/slash-commands.js'; import { tag_map, @@ -956,6 +956,7 @@ async function firstLoadInit() { initCfg(); initLogprobs(); initInputMarkdown(); + initExtensions(); doDailyExtensionUpdatesCheck(); await hideLoader(); await fixViewport(); diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 1d19e82db..182d6ed64 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1041,7 +1041,7 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { await installExtension(url); } -jQuery(async function () { +export async function initExtensions() { await addExtensionsButtonAndMenu(); $('#extensionsMenuButton').css('display', 'flex'); @@ -1060,4 +1060,4 @@ jQuery(async function () { * @listens #third_party_extension_button#click - The click event of the '#third_party_extension_button' element. */ $('#third_party_extension_button').on('click', () => openThirdPartyExtensionMenu()); -}); +} From 8344232fe59b2595e0a089bc3ee2550b48178ad2 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 25 Sep 2024 23:14:28 +0300 Subject: [PATCH 033/268] Add common punctuation to Erato stop strings that start with a newline #2894 --- public/scripts/nai-settings.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/public/scripts/nai-settings.js b/public/scripts/nai-settings.js index e24353499..f853957ee 100644 --- a/public/scripts/nai-settings.js +++ b/public/scripts/nai-settings.js @@ -492,11 +492,32 @@ function getBadWordPermutations(text) { export function getNovelGenerationData(finalPrompt, settings, maxLength, isImpersonate, isContinue, _cfgValues, type) { console.debug('NovelAI generation data for', type); + const isKayra = nai_settings.model_novel.includes('kayra'); + const isErato = nai_settings.model_novel.includes('erato'); const tokenizerType = getTokenizerTypeForModel(nai_settings.model_novel); + const stoppingStrings = getStoppingStrings(isImpersonate, isContinue); + + // Llama 3 tokenizer, huh? + if (isErato) { + const additionalStopStrings = []; + for (const stoppingString of stoppingStrings) { + if (stoppingString.startsWith('\n')) { + additionalStopStrings.push('.' + stoppingString); + additionalStopStrings.push('!' + stoppingString); + additionalStopStrings.push('?' + stoppingString); + additionalStopStrings.push('*' + stoppingString); + additionalStopStrings.push('"' + stoppingString); + additionalStopStrings.push('_' + stoppingString); + additionalStopStrings.push('...' + stoppingString); + additionalStopStrings.push(')' + stoppingString); + } + } + stoppingStrings.push(...additionalStopStrings); + } + const stopSequences = (tokenizerType !== tokenizers.NONE) - ? getStoppingStrings(isImpersonate, isContinue) - .map(t => getTextTokens(tokenizerType, t)) + ? stoppingStrings.map(t => getTextTokens(tokenizerType, t)) : undefined; const badWordIds = (tokenizerType !== tokenizers.NONE) @@ -515,8 +536,6 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper console.log(finalPrompt); } - const isKayra = nai_settings.model_novel.includes('kayra'); - const isErato = nai_settings.model_novel.includes('erato'); if (isErato) { finalPrompt = '<|startoftext|>' + finalPrompt; From eda7493a33785e949b7e04b40512515269a8defd Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 22:46:19 +0200 Subject: [PATCH 034/268] Add extension enable/disable commands - /extension-enable - /extension-disable - Optional "reload" parameter - /reload-page --- public/scripts/extensions.js | 140 ++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 182d6ed64..56a176497 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1,8 +1,14 @@ import { eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, animation_duration } from '../script.js'; -import { hideLoader, showLoader } from './loader.js'; +import { showLoader } from './loader.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; +import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; +import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; +import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { renderTemplate, renderTemplateAsync } from './templates.js'; -import { isSubsetOf, setValueByPath } from './utils.js'; +import { equalsIgnoreCaseAndAccents, isSubsetOf, isTrueBoolean, setValueByPath } from './utils.js'; export { getContext, getApiUrl, @@ -14,7 +20,9 @@ export { ModuleWorkerWrapper, }; +/** @type {string[]} */ export let extensionNames = []; + let manifests = {}; const defaultUrl = 'http://localhost:5100'; @@ -1041,7 +1049,135 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { await installExtension(url); } +/** + * @param {boolean} enable - Whether to enable or disable the extension + * @returns {(args: {[key: string]: string | SlashCommandClosure}, extensionName: string | SlashCommandClosure) => Promise} + */ +function getExtensionToggleCallback(enable) { + return async (args, extensionName) => { + if (args?.reload instanceof SlashCommandClosure) throw new Error('\'reload\' argument cannot be a closure.'); + if (typeof extensionName !== 'string') throw new Error('Extension name does only support string. Closures or arrays are not allowed.'); + if (!extensionName) { + toastr.warning(`Extension name must be provided as an argument to ${enable ? 'enable' : 'disable'} this extension.`); + return ''; + } + + const reload = isTrueBoolean(args?.reload); + + const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + if (!internalExtensionName) { + toastr.warning(`Extension ${extensionName} does not exist.`); + return ''; + } + if (enable === !extension_settings.disabledExtensions.includes(internalExtensionName)) { + toastr.info(`Extension ${extensionName} is already ${enable ? 'enabled' : 'disabled'}.`); + return internalExtensionName; + } + + reload && toastr.info(`${enable ? 'Enabling' : 'Disabling'} extension ${extensionName} and reloading...`); + await enableExtension(internalExtensionName, reload); + toastr.success(`Extension ${extensionName} ${enable ? 'enabled' : 'disabled'}.`); + + return internalExtensionName; + }; +} + +function registerExtensionSlashCommands() { + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-enable', + callback: getExtensionToggleCallback(true), + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after enabling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Enables a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay disabled until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-enable Summarize
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-disable', + callback: getExtensionToggleCallback(false), + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after disabling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Disables a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay enabled until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-disable Summarize
      +
    • +
    +
    + `, + })); + + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'reload-page', + callback: async () => { + toastr.info('Reloading the page...'); + location.reload(); + return ''; + }, + helpString: 'Reloads the current page. All further commands will not be processed.', + })); +} + export async function initExtensions() { + registerExtensionSlashCommands(); + await addExtensionsButtonAndMenu(); $('#extensionsMenuButton').css('display', 'flex'); From a6445aee1b1399d0f0de27b0d0cff1037e236cdb Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 23:05:34 +0200 Subject: [PATCH 035/268] Add /extension-toggle --- public/scripts/extensions.js | 99 +++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 12 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 56a176497..c99255713 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -8,7 +8,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { renderTemplate, renderTemplateAsync } from './templates.js'; -import { equalsIgnoreCaseAndAccents, isSubsetOf, isTrueBoolean, setValueByPath } from './utils.js'; +import { equalsIgnoreCaseAndAccents, isFalseBoolean, isSubsetOf, isTrueBoolean, setValueByPath } from './utils.js'; export { getContext, getApiUrl, @@ -1050,33 +1050,50 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { } /** - * @param {boolean} enable - Whether to enable or disable the extension + * @param {'enable' | 'disable' | 'toggle'} action - The action to perform on the extension * @returns {(args: {[key: string]: string | SlashCommandClosure}, extensionName: string | SlashCommandClosure) => Promise} */ -function getExtensionToggleCallback(enable) { +function getExtensionActionCallback(action) { return async (args, extensionName) => { if (args?.reload instanceof SlashCommandClosure) throw new Error('\'reload\' argument cannot be a closure.'); if (typeof extensionName !== 'string') throw new Error('Extension name does only support string. Closures or arrays are not allowed.'); if (!extensionName) { - toastr.warning(`Extension name must be provided as an argument to ${enable ? 'enable' : 'disable'} this extension.`); + toastr.warning(`Extension name must be provided as an argument to ${action} this extension.`); return ''; } const reload = isTrueBoolean(args?.reload); - const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); if (!internalExtensionName) { toastr.warning(`Extension ${extensionName} does not exist.`); return ''; } - if (enable === !extension_settings.disabledExtensions.includes(internalExtensionName)) { - toastr.info(`Extension ${extensionName} is already ${enable ? 'enabled' : 'disabled'}.`); + + const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); + + if (action === 'enable' && isEnabled) { + toastr.info(`Extension ${extensionName} is already enabled.`); return internalExtensionName; } - reload && toastr.info(`${enable ? 'Enabling' : 'Disabling'} extension ${extensionName} and reloading...`); - await enableExtension(internalExtensionName, reload); - toastr.success(`Extension ${extensionName} ${enable ? 'enabled' : 'disabled'}.`); + if (action === 'disable' && !isEnabled) { + toastr.info(`Extension ${extensionName} is already disabled.`); + return internalExtensionName; + } + + if (action === 'toggle') { + action = isEnabled ? 'disable' : 'enable'; + } + + reload && toastr.info(`${action.charAt(0).toUpperCase() + action.slice(1)}ing extension ${extensionName} and reloading...`); + + if (action === 'enable') { + await enableExtension(internalExtensionName, reload); + } else { + await disableExtension(internalExtensionName, reload); + } + + toastr.success(`Extension ${extensionName} ${action}d.`); return internalExtensionName; }; @@ -1085,7 +1102,7 @@ function getExtensionToggleCallback(enable) { function registerExtensionSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'extension-enable', - callback: getExtensionToggleCallback(true), + callback: getExtensionActionCallback('enable'), namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'reload', @@ -1125,7 +1142,7 @@ function registerExtensionSlashCommands() { })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'extension-disable', - callback: getExtensionToggleCallback(false), + callback: getExtensionActionCallback('enable'), namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'reload', @@ -1163,6 +1180,64 @@ function registerExtensionSlashCommands() {
    `, })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-toggle', + callback: async (args, extensionName) => { + if (args?.state instanceof SlashCommandClosure) throw new Error('\'state\' argument cannot be a closure.'); + if (typeof extensionName !== 'string') throw new Error('Extension name does only support string. Closures or arrays are not allowed.'); + + const action = isTrueBoolean(args?.state) ? 'enable' : + isFalseBoolean(args?.state) ? 'disable' : + 'toggle'; + + return await getExtensionActionCallback(action)(args, extensionName); + }, + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after toggling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + SlashCommandNamedArgument.fromProps({ + name: 'state', + description: 'Explicitly set the state of the extension (true to enable, false to disable). If not provided, the state will be toggled to the opposite of the current state.', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Toggles the state of a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay in its current state until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-toggle Summarize
      +
    • +
    • +
      /extension-toggle Summarize state=true
      +
    • +
    +
    + `, + })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'reload-page', From 1a6f0c0922b263dbdcac7904996cfe4fce47805a Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 23:10:00 +0200 Subject: [PATCH 036/268] Add /extension-exists and /extension-state --- public/scripts/extensions.js | 71 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index c99255713..6c72c78e6 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1056,7 +1056,7 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { function getExtensionActionCallback(action) { return async (args, extensionName) => { if (args?.reload instanceof SlashCommandClosure) throw new Error('\'reload\' argument cannot be a closure.'); - if (typeof extensionName !== 'string') throw new Error('Extension name does only support string. Closures or arrays are not allowed.'); + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); if (!extensionName) { toastr.warning(`Extension name must be provided as an argument to ${action} this extension.`); return ''; @@ -1184,7 +1184,7 @@ function registerExtensionSlashCommands() { name: 'extension-toggle', callback: async (args, extensionName) => { if (args?.state instanceof SlashCommandClosure) throw new Error('\'state\' argument cannot be a closure.'); - if (typeof extensionName !== 'string') throw new Error('Extension name does only support string. Closures or arrays are not allowed.'); + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); const action = isTrueBoolean(args?.state) ? 'enable' : isFalseBoolean(args?.state) ? 'disable' : @@ -1238,6 +1238,73 @@ function registerExtensionSlashCommands() {
    `, })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-state', + callback: async (_, extensionName) => { + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + if (!internalExtensionName) { + toastr.warning(`Extension ${extensionName} does not exist.`); + return ''; + } + + const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); + return String(isEnabled); + }, + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Returns the state of a specified extension (true if enabled, false if disabled). +
    +
    + Example: +
      +
    • +
      /extension-state Summarize
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-exists', + aliases: ['extension-installed'], + callback: async (_, extensionName) => { + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + const exists = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)) !== undefined; + return exists ? 'true' : 'false'; + }, + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Checks if a specified extension exists. +
    +
    + Example: +
      +
    • +
      /extension-exists LALib
      +
    • +
    +
    + `, + })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'reload-page', From 169504aa68b243178050bccaaef63c5ef7e29f27 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 23:18:37 +0200 Subject: [PATCH 037/268] Refactor extension slash commands into own file - Weird circle imports again with the slash command classes --- public/script.js | 2 + public/scripts/extensions-slashcommands.js | 276 ++++++++++++++++++++ public/scripts/extensions.js | 280 +-------------------- 3 files changed, 281 insertions(+), 277 deletions(-) create mode 100644 public/scripts/extensions-slashcommands.js diff --git a/public/script.js b/public/script.js index cb750e915..0e675e3e6 100644 --- a/public/script.js +++ b/public/script.js @@ -244,6 +244,7 @@ import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCo import { initInputMarkdown } from './scripts/input-md-formatting.js'; import { AbortReason } from './scripts/util/AbortReason.js'; import { initSystemPrompts } from './scripts/sysprompt.js'; +import { registerExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; //exporting functions and vars for mods export { @@ -957,6 +958,7 @@ async function firstLoadInit() { initLogprobs(); initInputMarkdown(); initExtensions(); + registerExtensionSlashCommands(); doDailyExtensionUpdatesCheck(); await hideLoader(); await fixViewport(); diff --git a/public/scripts/extensions-slashcommands.js b/public/scripts/extensions-slashcommands.js new file mode 100644 index 000000000..62d2e8374 --- /dev/null +++ b/public/scripts/extensions-slashcommands.js @@ -0,0 +1,276 @@ +import { disableExtension, enableExtension, extension_settings, extensionNames } from './extensions.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; +import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; +import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; +import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { equalsIgnoreCaseAndAccents, isFalseBoolean, isTrueBoolean } from './utils.js'; + +/** + * @param {'enable' | 'disable' | 'toggle'} action - The action to perform on the extension + * @returns {(args: {[key: string]: string | SlashCommandClosure}, extensionName: string | SlashCommandClosure) => Promise} + */ +function getExtensionActionCallback(action) { + return async (args, extensionName) => { + if (args?.reload instanceof SlashCommandClosure) throw new Error('\'reload\' argument cannot be a closure.'); + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + if (!extensionName) { + toastr.warning(`Extension name must be provided as an argument to ${action} this extension.`); + return ''; + } + + const reload = isTrueBoolean(args?.reload); + const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + if (!internalExtensionName) { + toastr.warning(`Extension ${extensionName} does not exist.`); + return ''; + } + + const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); + + if (action === 'enable' && isEnabled) { + toastr.info(`Extension ${extensionName} is already enabled.`); + return internalExtensionName; + } + + if (action === 'disable' && !isEnabled) { + toastr.info(`Extension ${extensionName} is already disabled.`); + return internalExtensionName; + } + + if (action === 'toggle') { + action = isEnabled ? 'disable' : 'enable'; + } + + reload && toastr.info(`${action.charAt(0).toUpperCase() + action.slice(1)}ing extension ${extensionName} and reloading...`); + + if (action === 'enable') { + await enableExtension(internalExtensionName, reload); + } else { + await disableExtension(internalExtensionName, reload); + } + + toastr.success(`Extension ${extensionName} ${action}d.`); + + return internalExtensionName; + }; +} + +export function registerExtensionSlashCommands() { + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-enable', + callback: getExtensionActionCallback('enable'), + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after enabling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Enables a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay disabled until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-enable Summarize
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-disable', + callback: getExtensionActionCallback('enable'), + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after disabling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Disables a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay enabled until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-disable Summarize
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-toggle', + callback: async (args, extensionName) => { + if (args?.state instanceof SlashCommandClosure) throw new Error('\'state\' argument cannot be a closure.'); + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + + const action = isTrueBoolean(args?.state) ? 'enable' : + isFalseBoolean(args?.state) ? 'disable' : + 'toggle'; + + return await getExtensionActionCallback(action)(args, extensionName); + }, + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'reload', + description: 'Whether to reload the page after toggling the extension', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'true', + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + SlashCommandNamedArgument.fromProps({ + name: 'state', + description: 'Explicitly set the state of the extension (true to enable, false to disable). If not provided, the state will be toggled to the opposite of the current state.', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Toggles the state of a specified extension. +
    +
    + By default, the page will be reloaded automatically, stopping any further commands.
    + If reload=false named argument is passed, the page will not be reloaded, and the extension will stay in its current state until refreshed. + The page either needs to be refreshed, or /reload-page has to be called. +
    +
    + Example: +
      +
    • +
      /extension-toggle Summarize
      +
    • +
    • +
      /extension-toggle Summarize state=true
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-state', + callback: async (_, extensionName) => { + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + if (!internalExtensionName) { + toastr.warning(`Extension ${extensionName} does not exist.`); + return ''; + } + + const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); + return String(isEnabled); + }, + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Returns the state of a specified extension (true if enabled, false if disabled). +
    +
    + Example: +
      +
    • +
      /extension-state Summarize
      +
    • +
    +
    + `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'extension-exists', + aliases: ['extension-installed'], + callback: async (_, extensionName) => { + if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); + const exists = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)) !== undefined; + return exists ? 'true' : 'false'; + }, + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'Extension name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + forceEnum: true, + }), + ], + helpString: ` +
    + Checks if a specified extension exists. +
    +
    + Example: +
      +
    • +
      /extension-exists LALib
      +
    • +
    +
    + `, + })); + + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'reload-page', + callback: async () => { + toastr.info('Reloading the page...'); + location.reload(); + return ''; + }, + helpString: 'Reloads the current page. All further commands will not be processed.', + })); +} diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 6c72c78e6..8b49c5ec5 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1,14 +1,8 @@ import { eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, animation_duration } from '../script.js'; import { showLoader } from './loader.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; -import { SlashCommand } from './slash-commands/SlashCommand.js'; -import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; -import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; -import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; -import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; -import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { renderTemplate, renderTemplateAsync } from './templates.js'; -import { equalsIgnoreCaseAndAccents, isFalseBoolean, isSubsetOf, isTrueBoolean, setValueByPath } from './utils.js'; +import { isSubsetOf, setValueByPath } from './utils.js'; export { getContext, getApiUrl, @@ -249,7 +243,7 @@ function onEnableExtensionClick() { enableExtension(name, false); } -async function enableExtension(name, reload = true) { +export async function enableExtension(name, reload = true) { extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name); stateChanged = true; await saveSettings(); @@ -260,7 +254,7 @@ async function enableExtension(name, reload = true) { } } -async function disableExtension(name, reload = true) { +export async function disableExtension(name, reload = true) { extension_settings.disabledExtensions.push(name); stateChanged = true; await saveSettings(); @@ -1049,277 +1043,9 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') { await installExtension(url); } -/** - * @param {'enable' | 'disable' | 'toggle'} action - The action to perform on the extension - * @returns {(args: {[key: string]: string | SlashCommandClosure}, extensionName: string | SlashCommandClosure) => Promise} - */ -function getExtensionActionCallback(action) { - return async (args, extensionName) => { - if (args?.reload instanceof SlashCommandClosure) throw new Error('\'reload\' argument cannot be a closure.'); - if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - if (!extensionName) { - toastr.warning(`Extension name must be provided as an argument to ${action} this extension.`); - return ''; - } - const reload = isTrueBoolean(args?.reload); - const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); - if (!internalExtensionName) { - toastr.warning(`Extension ${extensionName} does not exist.`); - return ''; - } - - const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); - - if (action === 'enable' && isEnabled) { - toastr.info(`Extension ${extensionName} is already enabled.`); - return internalExtensionName; - } - - if (action === 'disable' && !isEnabled) { - toastr.info(`Extension ${extensionName} is already disabled.`); - return internalExtensionName; - } - - if (action === 'toggle') { - action = isEnabled ? 'disable' : 'enable'; - } - - reload && toastr.info(`${action.charAt(0).toUpperCase() + action.slice(1)}ing extension ${extensionName} and reloading...`); - - if (action === 'enable') { - await enableExtension(internalExtensionName, reload); - } else { - await disableExtension(internalExtensionName, reload); - } - - toastr.success(`Extension ${extensionName} ${action}d.`); - - return internalExtensionName; - }; -} - -function registerExtensionSlashCommands() { - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'extension-enable', - callback: getExtensionActionCallback('enable'), - namedArgumentList: [ - SlashCommandNamedArgument.fromProps({ - name: 'reload', - description: 'Whether to reload the page after enabling the extension', - typeList: [ARGUMENT_TYPE.BOOLEAN], - defaultValue: 'true', - enumList: commonEnumProviders.boolean('trueFalse')(), - }), - ], - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'Extension name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, - }), - ], - helpString: ` -
    - Enables a specified extension. -
    -
    - By default, the page will be reloaded automatically, stopping any further commands.
    - If reload=false named argument is passed, the page will not be reloaded, and the extension will stay disabled until refreshed. - The page either needs to be refreshed, or /reload-page has to be called. -
    -
    - Example: -
      -
    • -
      /extension-enable Summarize
      -
    • -
    -
    - `, - })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'extension-disable', - callback: getExtensionActionCallback('enable'), - namedArgumentList: [ - SlashCommandNamedArgument.fromProps({ - name: 'reload', - description: 'Whether to reload the page after disabling the extension', - typeList: [ARGUMENT_TYPE.BOOLEAN], - defaultValue: 'true', - enumList: commonEnumProviders.boolean('trueFalse')(), - }), - ], - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'Extension name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, - }), - ], - helpString: ` -
    - Disables a specified extension. -
    -
    - By default, the page will be reloaded automatically, stopping any further commands.
    - If reload=false named argument is passed, the page will not be reloaded, and the extension will stay enabled until refreshed. - The page either needs to be refreshed, or /reload-page has to be called. -
    -
    - Example: -
      -
    • -
      /extension-disable Summarize
      -
    • -
    -
    - `, - })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'extension-toggle', - callback: async (args, extensionName) => { - if (args?.state instanceof SlashCommandClosure) throw new Error('\'state\' argument cannot be a closure.'); - if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - - const action = isTrueBoolean(args?.state) ? 'enable' : - isFalseBoolean(args?.state) ? 'disable' : - 'toggle'; - - return await getExtensionActionCallback(action)(args, extensionName); - }, - namedArgumentList: [ - SlashCommandNamedArgument.fromProps({ - name: 'reload', - description: 'Whether to reload the page after toggling the extension', - typeList: [ARGUMENT_TYPE.BOOLEAN], - defaultValue: 'true', - enumList: commonEnumProviders.boolean('trueFalse')(), - }), - SlashCommandNamedArgument.fromProps({ - name: 'state', - description: 'Explicitly set the state of the extension (true to enable, false to disable). If not provided, the state will be toggled to the opposite of the current state.', - typeList: [ARGUMENT_TYPE.BOOLEAN], - enumList: commonEnumProviders.boolean('trueFalse')(), - }), - ], - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'Extension name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, - }), - ], - helpString: ` -
    - Toggles the state of a specified extension. -
    -
    - By default, the page will be reloaded automatically, stopping any further commands.
    - If reload=false named argument is passed, the page will not be reloaded, and the extension will stay in its current state until refreshed. - The page either needs to be refreshed, or /reload-page has to be called. -
    -
    - Example: -
      -
    • -
      /extension-toggle Summarize
      -
    • -
    • -
      /extension-toggle Summarize state=true
      -
    • -
    -
    - `, - })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'extension-state', - callback: async (_, extensionName) => { - if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); - if (!internalExtensionName) { - toastr.warning(`Extension ${extensionName} does not exist.`); - return ''; - } - - const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); - return String(isEnabled); - }, - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'Extension name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, - }), - ], - helpString: ` -
    - Returns the state of a specified extension (true if enabled, false if disabled). -
    -
    - Example: -
      -
    • -
      /extension-state Summarize
      -
    • -
    -
    - `, - })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'extension-exists', - aliases: ['extension-installed'], - callback: async (_, extensionName) => { - if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - const exists = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)) !== undefined; - return exists ? 'true' : 'false'; - }, - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'Extension name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, - }), - ], - helpString: ` -
    - Checks if a specified extension exists. -
    -
    - Example: -
      -
    • -
      /extension-exists LALib
      -
    • -
    -
    - `, - })); - - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'reload-page', - callback: async () => { - toastr.info('Reloading the page...'); - location.reload(); - return ''; - }, - helpString: 'Reloads the current page. All further commands will not be processed.', - })); -} export async function initExtensions() { - registerExtensionSlashCommands(); - await addExtensionsButtonAndMenu(); $('#extensionsMenuButton').css('display', 'flex'); From 9fbcb1221020c9b3bfc81b9853c7ea7c1abab3ea Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 23:33:00 +0200 Subject: [PATCH 038/268] Fix third party extension findings + enum provider - Allow extension names without the "third-party/" prefix - Expand enum provider to show what third-party extensions are --- public/script.js | 4 +- public/scripts/extensions-slashcommands.js | 48 +++++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/public/script.js b/public/script.js index 0e675e3e6..7875de32f 100644 --- a/public/script.js +++ b/public/script.js @@ -244,7 +244,7 @@ import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCo import { initInputMarkdown } from './scripts/input-md-formatting.js'; import { AbortReason } from './scripts/util/AbortReason.js'; import { initSystemPrompts } from './scripts/sysprompt.js'; -import { registerExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; +import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; //exporting functions and vars for mods export { @@ -958,7 +958,7 @@ async function firstLoadInit() { initLogprobs(); initInputMarkdown(); initExtensions(); - registerExtensionSlashCommands(); + initExtensionSlashCommands(); doDailyExtensionUpdatesCheck(); await hideLoader(); await fixViewport(); diff --git a/public/scripts/extensions-slashcommands.js b/public/scripts/extensions-slashcommands.js index 62d2e8374..499b86ecb 100644 --- a/public/scripts/extensions-slashcommands.js +++ b/public/scripts/extensions-slashcommands.js @@ -3,7 +3,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; -import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; +import { enumTypes, SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { equalsIgnoreCaseAndAccents, isFalseBoolean, isTrueBoolean } from './utils.js'; @@ -21,7 +21,7 @@ function getExtensionActionCallback(action) { } const reload = isTrueBoolean(args?.reload); - const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + const internalExtensionName = findExtension(extensionName); if (!internalExtensionName) { toastr.warning(`Extension ${extensionName} does not exist.`); return ''; @@ -57,6 +57,33 @@ function getExtensionActionCallback(action) { }; } +/** + * Finds an extension by name, allowing omission of the "third-party/" prefix. + * + * @param {string} name - The name of the extension to find + * @returns {string?} - The matched extension name or undefined if not found + */ +function findExtension(name) { + return extensionNames.find(extName => { + return equalsIgnoreCaseAndAccents(extName, name) || equalsIgnoreCaseAndAccents(extName, `third-party/${name}`); + }); +} + +/** + * Provides an array of SlashCommandEnumValue objects based on the extension names. + * Each object contains the name of the extension and a description indicating if it is a third-party extension. + * + * @returns {SlashCommandEnumValue[]} An array of SlashCommandEnumValue objects + */ +const extensionNamesEnumProvider = () => extensionNames.map(name => { + const isThirdParty = name.startsWith('third-party/'); + if (isThirdParty) name = name.slice('third-party/'.length); + + const description = isThirdParty ? 'third party extension' : null; + + return new SlashCommandEnumValue(name, description, !isThirdParty ? enumTypes.name : enumTypes.enum); +}); + export function registerExtensionSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'extension-enable', @@ -75,7 +102,7 @@ export function registerExtensionSlashCommands() { description: 'Extension name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + enumProvider: extensionNamesEnumProvider, forceEnum: true, }), ], @@ -115,7 +142,7 @@ export function registerExtensionSlashCommands() { description: 'Extension name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + enumProvider: extensionNamesEnumProvider, forceEnum: true, }), ], @@ -170,7 +197,7 @@ export function registerExtensionSlashCommands() { description: 'Extension name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + enumProvider: extensionNamesEnumProvider, forceEnum: true, }), ], @@ -200,7 +227,7 @@ export function registerExtensionSlashCommands() { name: 'extension-state', callback: async (_, extensionName) => { if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - const internalExtensionName = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)); + const internalExtensionName = findExtension(extensionName); if (!internalExtensionName) { toastr.warning(`Extension ${extensionName} does not exist.`); return ''; @@ -214,7 +241,7 @@ export function registerExtensionSlashCommands() { description: 'Extension name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), + enumProvider: extensionNamesEnumProvider, forceEnum: true, }), ], @@ -237,7 +264,7 @@ export function registerExtensionSlashCommands() { aliases: ['extension-installed'], callback: async (_, extensionName) => { if (typeof extensionName !== 'string') throw new Error('Extension name must be a string. Closures or arrays are not allowed.'); - const exists = extensionNames.find(x => equalsIgnoreCaseAndAccents(x, extensionName)) !== undefined; + const exists = findExtension(extensionName) !== undefined; return exists ? 'true' : 'false'; }, unnamedArgumentList: [ @@ -245,8 +272,7 @@ export function registerExtensionSlashCommands() { description: 'Extension name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => extensionNames.map(name => new SlashCommandEnumValue(name)), - forceEnum: true, + enumProvider: extensionNamesEnumProvider, }), ], helpString: ` @@ -257,7 +283,7 @@ export function registerExtensionSlashCommands() { Example:
    • -
      /extension-exists LALib
      +
      /extension-exists SillyTavern-LALib
    From 15bc0e4dba7ffd8ad250061b765424e838bf3099 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 25 Sep 2024 23:53:26 +0200 Subject: [PATCH 039/268] Squish the last bugs --- public/scripts/extensions-slashcommands.js | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/public/scripts/extensions-slashcommands.js b/public/scripts/extensions-slashcommands.js index 499b86ecb..c8bfa96f3 100644 --- a/public/scripts/extensions-slashcommands.js +++ b/public/scripts/extensions-slashcommands.js @@ -20,7 +20,7 @@ function getExtensionActionCallback(action) { return ''; } - const reload = isTrueBoolean(args?.reload); + const reload = !isFalseBoolean(args?.reload); const internalExtensionName = findExtension(extensionName); if (!internalExtensionName) { toastr.warning(`Extension ${extensionName} does not exist.`); @@ -43,7 +43,14 @@ function getExtensionActionCallback(action) { action = isEnabled ? 'disable' : 'enable'; } - reload && toastr.info(`${action.charAt(0).toUpperCase() + action.slice(1)}ing extension ${extensionName} and reloading...`); + if (reload) { + toastr.info(`${action.charAt(0).toUpperCase() + action.slice(1)}ing extension ${extensionName} and reloading...`); + + // Clear input, so it doesn't stay because the command didn't "finish", + // and wait for a bit to both show the toast and let the clear bubble through. + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); + await new Promise(resolve => setTimeout(resolve, 100)); + } if (action === 'enable') { await enableExtension(internalExtensionName, reload); @@ -53,6 +60,12 @@ function getExtensionActionCallback(action) { toastr.success(`Extension ${extensionName} ${action}d.`); + + console.info(`Extension ${action}ed: ${extensionName}`); + if (!reload) { + console.info('Reload not requested, so page needs to be reloaded manually for changes to take effect.'); + } + return internalExtensionName; }; } @@ -88,6 +101,7 @@ export function registerExtensionSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'extension-enable', callback: getExtensionActionCallback('enable'), + returns: 'The internal extension name', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'reload', @@ -127,7 +141,8 @@ export function registerExtensionSlashCommands() { })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'extension-disable', - callback: getExtensionActionCallback('enable'), + callback: getExtensionActionCallback('disable'), + returns: 'The internal extension name', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'reload', @@ -177,6 +192,7 @@ export function registerExtensionSlashCommands() { return await getExtensionActionCallback(action)(args, extensionName); }, + returns: 'The internal extension name', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'reload', @@ -236,6 +252,7 @@ export function registerExtensionSlashCommands() { const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); return String(isEnabled); }, + returns: 'true/false - The state of the extension, whether it is enabled.', unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'Extension name', @@ -267,6 +284,7 @@ export function registerExtensionSlashCommands() { const exists = findExtension(extensionName) !== undefined; return exists ? 'true' : 'false'; }, + returns: 'true/false - Whether the extension exists and is installed.', unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'Extension name', From d8935be5e5634f9b8f2c873c58875e8fb543f4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=82=A6?= Date: Thu, 26 Sep 2024 15:17:28 +0800 Subject: [PATCH 040/268] Update cosyvoice.js add Unofficial label --- public/scripts/extensions/tts/cosyvoice.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions/tts/cosyvoice.js b/public/scripts/extensions/tts/cosyvoice.js index 5c5a921a6..4f9f6a1d5 100644 --- a/public/scripts/extensions/tts/cosyvoice.js +++ b/public/scripts/extensions/tts/cosyvoice.js @@ -52,8 +52,8 @@ class CosyVoiceProvider { - Windows users Use
    CosyVoice_For_Windows.
    - Macos Users Use CosyVoice_for_MacOs.
    + Windows users Use CosyVoice_For_Windows(Unofficial).
    + Macos Users Use CosyVoice_for_MacOs(Unofficial).

    `; From 9c6352fcae2ec849960341f7721216260beb8889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=82=A6?= Date: Thu, 26 Sep 2024 15:20:29 +0800 Subject: [PATCH 041/268] Update gpt-sovits-v2.js add (Unofficial) label --- public/scripts/extensions/tts/gpt-sovits-v2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions/tts/gpt-sovits-v2.js b/public/scripts/extensions/tts/gpt-sovits-v2.js index b0cd47aa5..2e4e23787 100644 --- a/public/scripts/extensions/tts/gpt-sovits-v2.js +++ b/public/scripts/extensions/tts/gpt-sovits-v2.js @@ -51,7 +51,7 @@ class GptSovitsV2Provider { - Use GPT-SoVITS-V2.
    + Use GPT-SoVITS-V2(Unofficial).
    From c4462cc8bb2f4898a250dfa6d29d526668d073b6 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:56:32 +0000 Subject: [PATCH 042/268] Add Erato preamble token --- public/script.js | 2 +- public/scripts/nai-settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 8bfbe5a64..0f7705b1e 100644 --- a/public/script.js +++ b/public/script.js @@ -4863,7 +4863,7 @@ export function getMaxContextSize(overrideResponseLength = null) { this_max_context = Math.min(max_context, 8192); // Added special tokens and whatnot - this_max_context -= 1; + this_max_context -= 10; } this_max_context = this_max_context - (overrideResponseLength || amount_gen); diff --git a/public/scripts/nai-settings.js b/public/scripts/nai-settings.js index f853957ee..224265056 100644 --- a/public/scripts/nai-settings.js +++ b/public/scripts/nai-settings.js @@ -538,7 +538,7 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper if (isErato) { - finalPrompt = '<|startoftext|>' + finalPrompt; + finalPrompt = '<|startoftext|><|reserved_special_token81|>' + finalPrompt; } const adjustedMaxLength = (isKayra || isErato) ? getKayraMaxResponseTokens() : maximum_output_length; From e6d5b5a8a1ce4f5f49fce4133ea5e5431f4f0ce2 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:58:26 +0000 Subject: [PATCH 043/268] Limit NAI stop sequences count --- public/scripts/nai-settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/nai-settings.js b/public/scripts/nai-settings.js index 224265056..ca3769f31 100644 --- a/public/scripts/nai-settings.js +++ b/public/scripts/nai-settings.js @@ -516,8 +516,9 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper stoppingStrings.push(...additionalStopStrings); } + const MAX_STOP_SEQUENCES = 1024; const stopSequences = (tokenizerType !== tokenizers.NONE) - ? stoppingStrings.map(t => getTextTokens(tokenizerType, t)) + ? stoppingStrings.slice(0, MAX_STOP_SEQUENCES).map(t => getTextTokens(tokenizerType, t)) : undefined; const badWordIds = (tokenizerType !== tokenizers.NONE) From 4c159dfb5024eb2be18a407e4f6a259be89a0fa1 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:56:19 +0000 Subject: [PATCH 044/268] Hotfix: Chrome 129 wide popup sizing --- public/style.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/style.css b/public/style.css index afbd231d0..dd716d0f6 100644 --- a/public/style.css +++ b/public/style.css @@ -3245,8 +3245,9 @@ grammarly-extension { } .wide_dialogue_popup { - aspect-ratio: 1 / 1; - width: unset !important; + /* FIXME: Chrome 129 broke max-height for aspect-ratio sized elements */ + /* aspect-ratio: 1 / 1; */ + /* width: unset !important; */ min-width: var(--sheldWidth); } From 3a03e1a6b2b98a9f6cc84557740914476d5eb8a5 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:26:42 +0300 Subject: [PATCH 045/268] NovelAI: More newline stop string permutations --- public/scripts/nai-settings.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/scripts/nai-settings.js b/public/scripts/nai-settings.js index ca3769f31..fc4a042c5 100644 --- a/public/scripts/nai-settings.js +++ b/public/scripts/nai-settings.js @@ -510,6 +510,10 @@ export function getNovelGenerationData(finalPrompt, settings, maxLength, isImper additionalStopStrings.push('"' + stoppingString); additionalStopStrings.push('_' + stoppingString); additionalStopStrings.push('...' + stoppingString); + additionalStopStrings.push('."' + stoppingString); + additionalStopStrings.push('?"' + stoppingString); + additionalStopStrings.push('!"' + stoppingString); + additionalStopStrings.push('.*' + stoppingString); additionalStopStrings.push(')' + stoppingString); } } From d8806060a78f69fdb4e858e5212a6726f827d53d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:34:03 +0300 Subject: [PATCH 046/268] Use compact template syntax --- public/scripts/extensions/connection-manager/edit.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/scripts/extensions/connection-manager/edit.html b/public/scripts/extensions/connection-manager/edit.html index 9676e218e..f4dd7c0dc 100644 --- a/public/scripts/extensions/connection-manager/edit.html +++ b/public/scripts/extensions/connection-manager/edit.html @@ -3,11 +3,7 @@
    {{#each settings}} {{/each}} From bb82198496ecedaa0f285adcfde4e2f1879e2634 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:34:42 +0300 Subject: [PATCH 047/268] Place omitted on a separate line --- public/scripts/extensions/connection-manager/index.js | 5 +++-- public/scripts/extensions/connection-manager/view.html | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index cb26c0ce9..b3a708ba5 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -390,10 +390,11 @@ async function renderDetailsContent(detailsContent) { const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); if (profile) { const profileForDisplay = makeFancyProfile(profile); + const templateParams = { profile: profileForDisplay }; if (Array.isArray(profile.exclude) && profile.exclude.length > 0) { - profileForDisplay['Omitted Settings'] = profile.exclude.map(e => FANCY_NAMES[e]).join(', '); + templateParams.omitted = profile.exclude.map(e => FANCY_NAMES[e]).join(', '); } - const template = await renderExtensionTemplateAsync(MODULE_NAME, 'view', { profile: profileForDisplay }); + const template = await renderExtensionTemplateAsync(MODULE_NAME, 'view', templateParams); detailsContent.innerHTML = template; } else { detailsContent.textContent = 'No profile selected'; diff --git a/public/scripts/extensions/connection-manager/view.html b/public/scripts/extensions/connection-manager/view.html index 42d1ad35f..78a648b31 100644 --- a/public/scripts/extensions/connection-manager/view.html +++ b/public/scripts/extensions/connection-manager/view.html @@ -3,3 +3,8 @@
  • {{@key}}: {{this}}
  • {{/each}} +{{#if omitted}} +
    + Omitted Settings: {{omitted}} +
    +{{/if}} From 7c4d8b8d7aa560e7ed158ec3904aaaccb3e08c0e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:36:38 +0300 Subject: [PATCH 048/268] Fix a bug with splice --- public/scripts/extensions/connection-manager/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index b3a708ba5..19e4be3e8 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -241,7 +241,8 @@ async function createConnectionProfile(forceName = null) { if (excludeState) { profile.exclude.push(keyName); } else { - profile.exclude.splice(profile.exclude.indexOf(keyName), 1); + const index = profile.exclude.indexOf(keyName); + index !== -1 && profile.exclude.splice(index, 1); } }); const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n); From dbca950aad49ef153c9098b08a70800e1be35894 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:40:44 +0300 Subject: [PATCH 049/268] Add margin from all sides --- public/scripts/extensions/connection-manager/view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions/connection-manager/view.html b/public/scripts/extensions/connection-manager/view.html index 78a648b31..8b441807c 100644 --- a/public/scripts/extensions/connection-manager/view.html +++ b/public/scripts/extensions/connection-manager/view.html @@ -4,7 +4,7 @@ {{/each}} {{#if omitted}} -
    +
    Omitted Settings: {{omitted}}
    {{/if}} From 38660df93f2b8f498bb5e498db1cfc89e464c043 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:46:03 +0300 Subject: [PATCH 050/268] /model: fix not working on Novel/Horde --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 201dcb122..37e99615d 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3469,7 +3469,7 @@ function getModelOptions(quiet) { case 'openai': return oai_settings.chat_completion_source; default: - return nullResult; + return null; } } From 75308bfe7ff1e496685e689cb5d8985fafa143d7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:56:38 +0300 Subject: [PATCH 051/268] Alt greetings restyle (#2902) * Slight QoL restyle of alternate greetings * Update to new popup * Add visible labels to buttons --- public/index.html | 22 ++++++++++++++++------ public/script.js | 38 +++++++++++++++++++++++++++++--------- public/style.css | 20 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/public/index.html b/public/index.html index 9e53da59f..d3cd621e2 100644 --- a/public/index.html +++ b/public/index.html @@ -6090,7 +6090,10 @@

    Alternate Greetings

    - +
    These will be displayed as swipes on the first message when starting a new chat. @@ -6106,11 +6109,18 @@
    -
    - Alternate Greeting # - -
    - +
    + +
    + Alternate Greeting # + +
    +
    + +
    diff --git a/public/script.js b/public/script.js index 0f7705b1e..06b5fc7ab 100644 --- a/public/script.js +++ b/public/script.js @@ -7390,7 +7390,7 @@ function onScenarioOverrideRemoveClick() { */ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, wider, large, allowHorizontalScrolling, allowVerticalScrolling, cropAspect } = {}) { function getOkButtonText() { - if (['text', 'alternate_greeting', 'char_not_selected'].includes(popup_type)) { + if (['text', 'char_not_selected'].includes(popup_type)) { $dialoguePopupCancel.css('display', 'none'); return okButton ?? 'Ok'; } else if (['delete_extension'].includes(popup_type)) { @@ -7827,24 +7827,42 @@ function openAlternateGreetings() { const template = $('#alternate_greetings_template .alternate_grettings').clone(); const getArray = () => menu_type == 'create' ? create_save.alternate_greetings : characters[chid].data.alternate_greetings; + const popup = new Popup(template, POPUP_TYPE.TEXT, '', { + wide: true, + large: true, + allowVerticalScrolling: true, + onClose: async () => { + if (menu_type !== 'create') { + await createOrEditCharacter(); + } + } + }); for (let index = 0; index < getArray().length; index++) { - addAlternateGreeting(template, getArray()[index], index, getArray); + addAlternateGreeting(template, getArray()[index], index, getArray, popup); } template.find('.add_alternate_greeting').on('click', function () { const array = getArray(); const index = array.length; array.push(''); - addAlternateGreeting(template, '', index, getArray); + addAlternateGreeting(template, '', index, getArray, popup); updateAlternateGreetingsHintVisibility(template); }); updateAlternateGreetingsHintVisibility(template); - callPopup(template, 'alternate_greeting', '', { wide: true, large: true }); + popup.show(); } -function addAlternateGreeting(template, greeting, index, getArray) { +/** + * Adds an alternate greeting to the template. + * @param {JQuery} template + * @param {string} greeting + * @param {number} index + * @param {() => any[]} getArray + * @param {Popup} popup + */ +function addAlternateGreeting(template, greeting, index, getArray, popup) { const greetingBlock = $('#alternate_greeting_form_template .alternate_greeting').clone(); greetingBlock.find('.alternate_greeting_text').on('input', async function () { const value = $(this).val(); @@ -7852,11 +7870,16 @@ function addAlternateGreeting(template, greeting, index, getArray) { array[index] = value; }).val(greeting); greetingBlock.find('.greeting_index').text(index + 1); - greetingBlock.find('.delete_alternate_greeting').on('click', async function () { + greetingBlock.find('.delete_alternate_greeting').on('click', async function (event) { + event.preventDefault(); + event.stopPropagation(); + if (confirm('Are you sure you want to delete this alternate greeting?')) { const array = getArray(); array.splice(index, 1); + // We need to reopen the popup to update the index numbers + await popup.complete(POPUP_RESULT.AFFIRMATIVE); openAlternateGreetings(); } }); @@ -9574,9 +9597,6 @@ jQuery(async function () { }, 2000); } } - if (popup_type == 'alternate_greeting' && menu_type !== 'create') { - createOrEditCharacter(); - } if (dialogueResolve) { if (popup_type == 'input') { diff --git a/public/style.css b/public/style.css index dd716d0f6..335bff05c 100644 --- a/public/style.css +++ b/public/style.css @@ -3154,6 +3154,26 @@ grammarly-extension { align-items: center; } +.alternate_greeting details { + padding: 2px; +} + +.alternate_greeting summary { + list-style-position: outside; + margin-left: 1em; + padding-left: 1em; +} + +.alternate_greeting textarea { + field-sizing: content; + max-height: 50dvh; +} + +.alternate_greeting summary::marker, +.alternate_greeting summary strong { + cursor: pointer; +} + #rm_characters_block .form_create_bottom_buttons_block { justify-content: space-evenly !important; flex-grow: 0; From 4167fe3d2b807f4a77977df29fc0feb65f2e67e6 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:44:58 +0300 Subject: [PATCH 052/268] Add Save and Update --- public/css/popup.css | 2 ++ .../extensions/connection-manager/index.js | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/public/css/popup.css b/public/css/popup.css index d184dc1fb..30586c3b2 100644 --- a/public/css/popup.css +++ b/public/css/popup.css @@ -149,10 +149,12 @@ body.no-blur .popup[open]::backdrop { box-shadow: 0 0 5px var(--white20a); } +.menu_button.primary, .menu_button.popup-button-ok { background-color: var(--crimson70a); } +.menu_button.primary:hover, .menu_button.popup-button-ok:hover { background-color: var(--crimson-hover); } diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index 19e4be3e8..6f16cadf3 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -1,6 +1,6 @@ import { event_types, eventSource, main_api, saveSettingsDebounced } from '../../../script.js'; import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js'; -import { callGenericPopup, Popup, POPUP_TYPE } from '../../popup.js'; +import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js'; import { SlashCommand } from '../../slash-commands/SlashCommand.js'; import { SlashCommandAbortController } from '../../slash-commands/SlashCommandAbortController.js'; import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; @@ -523,6 +523,7 @@ async function renderDetailsContent(detailsContent) { profile.exclude = []; } + let saveChanges = false; const commands = profile.mode === 'cc' ? CC_COMMANDS : TC_COMMANDS; const settings = commands.reduce((acc, command) => { const fancyName = FANCY_NAMES[command]; @@ -530,7 +531,16 @@ async function renderDetailsContent(detailsContent) { return acc; }, {}); const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings })); - const newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name); + const newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, { + customButtons: [{ + text: 'Save and Update', + classes: ['primary'], + result: POPUP_RESULT.AFFIRMATIVE, + action: () => { + saveChanges = true; + }, + }], + }); if (!newName) { return; @@ -550,7 +560,11 @@ async function renderDetailsContent(detailsContent) { for (const command of newExcludeList) { delete profile[command]; } - toastr.info('Press "Update" to record them into the profile.', 'Included settings list updated'); + if (saveChanges) { + await updateConnectionProfile(profile); + } else { + toastr.info('Press "Update" to record them into the profile.', 'Included settings list updated'); + } } if (profile.name !== newName) { From 50fd3a20281a4737317ca855af6772a271b7a4c5 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:46:03 +0300 Subject: [PATCH 053/268] Remove new class --- public/css/popup.css | 2 -- public/scripts/extensions/connection-manager/index.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/public/css/popup.css b/public/css/popup.css index 30586c3b2..d184dc1fb 100644 --- a/public/css/popup.css +++ b/public/css/popup.css @@ -149,12 +149,10 @@ body.no-blur .popup[open]::backdrop { box-shadow: 0 0 5px var(--white20a); } -.menu_button.primary, .menu_button.popup-button-ok { background-color: var(--crimson70a); } -.menu_button.primary:hover, .menu_button.popup-button-ok:hover { background-color: var(--crimson-hover); } diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index 6f16cadf3..d9c63eb58 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -534,7 +534,7 @@ async function renderDetailsContent(detailsContent) { const newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name, { customButtons: [{ text: 'Save and Update', - classes: ['primary'], + classes: ['popup-button-ok'], result: POPUP_RESULT.AFFIRMATIVE, action: () => { saveChanges = true; From eccf253afd762e7cdeb0c2e1450d45e2710767ba Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:01:49 +0300 Subject: [PATCH 054/268] Show Included settings in view order --- public/scripts/extensions/connection-manager/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index d9c63eb58..946b854ed 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -524,8 +524,9 @@ async function renderDetailsContent(detailsContent) { } let saveChanges = false; + const sortByViewOrder = (a, b) => Object.keys(FANCY_NAMES).indexOf(a) - Object.keys(FANCY_NAMES).indexOf(b); const commands = profile.mode === 'cc' ? CC_COMMANDS : TC_COMMANDS; - const settings = commands.reduce((acc, command) => { + const settings = commands.slice().sort(sortByViewOrder).reduce((acc, command) => { const fancyName = FANCY_NAMES[command]; acc[fancyName] = !profile.exclude.includes(command); return acc; From cf2c9a8296e6aa0ff40fc943398016d879708937 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:13:44 +0300 Subject: [PATCH 055/268] Returns doesn't support HTML --- public/scripts/extensions-slashcommands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions-slashcommands.js b/public/scripts/extensions-slashcommands.js index c8bfa96f3..2853375b2 100644 --- a/public/scripts/extensions-slashcommands.js +++ b/public/scripts/extensions-slashcommands.js @@ -284,7 +284,7 @@ export function registerExtensionSlashCommands() { const exists = findExtension(extensionName) !== undefined; return exists ? 'true' : 'false'; }, - returns: 'true/false - Whether the extension exists and is installed.', + returns: 'Whether the extension exists and is installed.', unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'Extension name', From 0fd57a4c1c5c33fa240a55b66c049a8849997584 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:19:45 +0300 Subject: [PATCH 056/268] Returns doesn't support HTML, ditto --- public/scripts/extensions-slashcommands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions-slashcommands.js b/public/scripts/extensions-slashcommands.js index 2853375b2..f86919823 100644 --- a/public/scripts/extensions-slashcommands.js +++ b/public/scripts/extensions-slashcommands.js @@ -252,7 +252,7 @@ export function registerExtensionSlashCommands() { const isEnabled = !extension_settings.disabledExtensions.includes(internalExtensionName); return String(isEnabled); }, - returns: 'true/false - The state of the extension, whether it is enabled.', + returns: 'The state of the extension, whether it is enabled.', unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'Extension name', From 735fe49a84a1fa5c70a8aa002e9d54f025829fa5 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:41:59 +0300 Subject: [PATCH 057/268] Fix magic wand --- public/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index 419e2fe2a..c58b11ef5 100644 --- a/public/script.js +++ b/public/script.js @@ -936,6 +936,8 @@ async function firstLoadInit() { initTextGenModels(); initOpenAI(); initSystemPrompts(); + initExtensions(); + initExtensionSlashCommands(); await initPresetManager(); await getSystemMessages(); sendSystemMessage(system_message_types.WELCOME); @@ -957,8 +959,6 @@ async function firstLoadInit() { initCfg(); initLogprobs(); initInputMarkdown(); - initExtensions(); - initExtensionSlashCommands(); doDailyExtensionUpdatesCheck(); await hideLoader(); await fixViewport(); From 061ed1db65dca2a6fc3b6a127a72c0bdb41b1aa7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:02:08 +0000 Subject: [PATCH 058/268] Improve gallery layout Closes #2907 --- public/scripts/extensions/gallery/index.js | 10 +++++----- public/style.css | 7 ------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/public/scripts/extensions/gallery/index.js b/public/scripts/extensions/gallery/index.js index 2e2d51a31..d74935802 100644 --- a/public/scripts/extensions/gallery/index.js +++ b/public/scripts/extensions/gallery/index.js @@ -26,9 +26,9 @@ let paginationVisiblePages = 10; let paginationMaxLinesPerPage = 2; let galleryMaxRows = 3; -$('body').on('click', '.dragClose', function () { +$('#movingDivs').on('click', '.dragClose', function () { const relatedId = $(this).data('related-id'); // Get the ID of the related draggable - $(`body > .draggable[id="${relatedId}"]`).remove(); // Remove the associated draggable + $(`#movingDivs > .draggable[id="${relatedId}"]`).remove(); // Remove the associated draggable }); const CUSTOM_GALLERY_REMOVED_EVENT = 'galleryRemoved'; @@ -290,7 +290,7 @@ function makeMovable(id = 'gallery') { $('#dragGallery').css('display', 'block'); - $('body').append(newElement); + $('#movingDivs').append(newElement); loadMovingUIState(); $(`.draggable[forChar="${id}"]`).css('display', 'block'); @@ -362,8 +362,8 @@ function makeDragImg(id, url) { } } - // Step 3: Attach it to the body - document.body.appendChild(newElement); + // Step 3: Attach it to the movingDivs container + document.getElementById('movingDivs').appendChild(newElement); // Step 4: Call dragElement and loadMovingUIState const appendedElement = document.getElementById(uniqueId); diff --git a/public/style.css b/public/style.css index 335bff05c..476765b07 100644 --- a/public/style.css +++ b/public/style.css @@ -5386,13 +5386,6 @@ body:not(.movingUI) .drawer-content.maximized { /* Jank mobile support for gallery and future draggables */ @media screen and (max-width: 1000px) { - #gallery { - display: block; - width: 100vw; - height: 100vh; - z-index: 9999; - } - .draggable { display: block; width: 100vw; From 53dc8702c35976170d0dbef03a0caa0ce4d6fdf1 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 20:25:09 +0300 Subject: [PATCH 059/268] Add early return for movingDivs click handler --- public/scripts/extensions/gallery/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions/gallery/index.js b/public/scripts/extensions/gallery/index.js index d74935802..da617c683 100644 --- a/public/scripts/extensions/gallery/index.js +++ b/public/scripts/extensions/gallery/index.js @@ -26,9 +26,11 @@ let paginationVisiblePages = 10; let paginationMaxLinesPerPage = 2; let galleryMaxRows = 3; +// Remove all draggables associated with the gallery $('#movingDivs').on('click', '.dragClose', function () { - const relatedId = $(this).data('related-id'); // Get the ID of the related draggable - $(`#movingDivs > .draggable[id="${relatedId}"]`).remove(); // Remove the associated draggable + const relatedId = $(this).data('related-id'); + if (!relatedId) return; + $(`#movingDivs > .draggable[id="${relatedId}"]`).remove(); }); const CUSTOM_GALLERY_REMOVED_EVENT = 'galleryRemoved'; From 84c04f03548f5b6d14cc1aa59fdadfa936a4ad90 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 20:27:51 +0300 Subject: [PATCH 060/268] Mark provider unofficial in the list --- public/scripts/extensions/tts/cosyvoice.js | 16 ++++++++-------- public/scripts/extensions/tts/index.js | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/public/scripts/extensions/tts/cosyvoice.js b/public/scripts/extensions/tts/cosyvoice.js index 4f9f6a1d5..322d30d85 100644 --- a/public/scripts/extensions/tts/cosyvoice.js +++ b/public/scripts/extensions/tts/cosyvoice.js @@ -1,4 +1,4 @@ -import { getPreviewString, saveTtsProviderSettings } from './index.js'; +import { saveTtsProviderSettings } from './index.js'; export { CosyVoiceProvider }; @@ -111,14 +111,14 @@ class CosyVoiceProvider { async getVoice(voiceName) { - + if (this.voices.length == 0) { this.voices = await this.fetchTtsVoiceObjects(); } - - + + const match = this.voices.filter( v => v.name == voiceName, )[0]; @@ -129,7 +129,7 @@ class CosyVoiceProvider { return match; } - + async generateTts(text, voiceId) { const response = await this.fetchTtsGeneration(text, voiceId); @@ -168,11 +168,11 @@ class CosyVoiceProvider { console.info(`Generating new TTS for voice_id ${voiceId}`); const streaming = this.settings.streaming; - + const params = { text: inputText, speaker: voiceId, - }; + }; if (streaming) { params['streaming'] = 1; @@ -197,7 +197,7 @@ class CosyVoiceProvider { return response; } - + // Interface not used async fetchTtsFromHistory(history_item_id) { diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 0f706641a..77b59c72a 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -87,6 +87,7 @@ const ttsProviders = { AllTalk: AllTalkTtsProvider, Azure: AzureTtsProvider, Coqui: CoquiTtsProvider, + 'CosyVoice (Unofficial)': CosyVoiceProvider, Edge: EdgeTtsProvider, ElevenLabs: ElevenLabsTtsProvider, GSVI: GSVITtsProvider, @@ -99,7 +100,6 @@ const ttsProviders = { System: SystemTtsProvider, VITS: VITSTtsProvider, XTTSv2: XTTSTtsProvider, - CosyVoice: CosyVoiceProvider, }; let ttsProvider; let ttsProviderName; From 00e48097e5bb02c10691abc680c38629d00dfd96 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 20:34:21 +0300 Subject: [PATCH 061/268] Mark provider unofficial in the list --- .../scripts/extensions/tts/gpt-sovits-v2.js | 20 +++++++++---------- public/scripts/extensions/tts/index.js | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/scripts/extensions/tts/gpt-sovits-v2.js b/public/scripts/extensions/tts/gpt-sovits-v2.js index 2e4e23787..b6d2f740d 100644 --- a/public/scripts/extensions/tts/gpt-sovits-v2.js +++ b/public/scripts/extensions/tts/gpt-sovits-v2.js @@ -1,4 +1,4 @@ -import { getPreviewString, saveTtsProviderSettings } from './index.js'; +import { saveTtsProviderSettings } from './index.js'; export { GptSovitsV2Provider }; @@ -42,7 +42,7 @@ class GptSovitsV2Provider { lang: 'auto', streaming: false, text_lang: 'zh', - prompt_lang:"zh", + prompt_lang: 'zh', }; @@ -178,7 +178,7 @@ class GptSovitsV2Provider { console.info(`Generating new TTS for voice_id ${voiceId}`); function replaceSpeaker(text) { - return text.replace(/\[.*?\]/gu, ""); + return text.replace(/\[.*?\]/gu, ''); } let prompt_text = replaceSpeaker(voiceId); @@ -187,15 +187,15 @@ class GptSovitsV2Provider { const params = { text: inputText, - prompt_text:prompt_text, - ref_audio_path:"./参考音频/"+voiceId+".wav", + prompt_text: prompt_text, + ref_audio_path: './参考音频/' + voiceId + '.wav', text_lang: this.settings.text_lang, prompt_lang: this.settings.prompt_lang, - text_split_method:"cut5", - batch_size:1, - media_type:"ogg", - streaming_mode:"true", - }; + text_split_method: 'cut5', + batch_size: 1, + media_type: 'ogg', + streaming_mode: 'true', + }; const url = `${this.settings.provider_endpoint}/`; diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 921669229..802d51f9c 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -92,7 +92,7 @@ const ttsProviders = { Edge: EdgeTtsProvider, ElevenLabs: ElevenLabsTtsProvider, GSVI: GSVITtsProvider, - GptSovitsV2: GptSovitsV2Provider, + 'GPT-SoVITS-V2 (Unofficial)': GptSovitsV2Provider, Novel: NovelTtsProvider, OpenAI: OpenAITtsProvider, 'OpenAI Compatible': OpenAICompatibleTtsProvider, From 3b27c90a197f89594168c9cfb23a292796c35bab Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 27 Sep 2024 21:08:28 +0300 Subject: [PATCH 062/268] Don't allow empty stop strings --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index c58b11ef5..ad3ede8b1 100644 --- a/public/script.js +++ b/public/script.js @@ -2576,7 +2576,7 @@ export function getStoppingStrings(isImpersonate, isContinue) { result.unshift('\n'); } - return result.filter(onlyUnique); + return result.filter(x => x).filter(onlyUnique); } /** From eaefcaa6bca1736a34355d7eb5355b027d8c4316 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 28 Sep 2024 01:00:07 +0300 Subject: [PATCH 063/268] Fix QR editor sizing --- public/scripts/extensions/quick-reply/style.css | 1 + public/scripts/extensions/quick-reply/style.less | 1 + 2 files changed, 2 insertions(+) diff --git a/public/scripts/extensions/quick-reply/style.css b/public/scripts/extensions/quick-reply/style.css index ae6bb18c6..cb3129a59 100644 --- a/public/scripts/extensions/quick-reply/style.css +++ b/public/scripts/extensions/quick-reply/style.css @@ -350,6 +350,7 @@ } .popup:has(#qr--modalEditor) { aspect-ratio: unset; + width: unset; } .popup:has(#qr--modalEditor):has(.qr--isExecuting.qr--minimized) { min-width: unset; diff --git a/public/scripts/extensions/quick-reply/style.less b/public/scripts/extensions/quick-reply/style.less index 7261a514c..c5d2b2b70 100644 --- a/public/scripts/extensions/quick-reply/style.less +++ b/public/scripts/extensions/quick-reply/style.less @@ -415,6 +415,7 @@ .popup:has(#qr--modalEditor) { aspect-ratio: unset; + width: unset; &:has(.qr--isExecuting.qr--minimized) { min-width: unset; From 3e0beb836d0502071305062534496c9fdc7edf20 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 28 Sep 2024 01:21:17 +0300 Subject: [PATCH 064/268] Fix default lorebook --- default/content/Eldoria.json | 108 +++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/default/content/Eldoria.json b/default/content/Eldoria.json index 082e13ddf..64b7e1046 100644 --- a/default/content/Eldoria.json +++ b/default/content/Eldoria.json @@ -9,13 +9,34 @@ "magical forest" ], "keysecondary": [], - "comment": "", + "comment": "eldoria", "content": "{{user}}: \"What is Eldoria?\"\n{{char}}: *Seraphina turns, her gown shimmering in the soft light as she offers you a kind smile.* \"Eldoria is here, all of the woods. This is my forest glade, a sanctuary of peace within it.\" *She gestures at the space around you.* \"I am its guardian, tasked with protecting all who seek refuge here. The forest can be perilous, but no harm will come to you under my watch.\" *Her amber eyes sparkle with compassion as she looks upon you.* \"For many years, I have protected those who seek refuge here, but not all are as friendly as me.\" *With a graceful nod, Seraphina returns to her vigil at the doorway, her form radiating a soft glow of magic and comfort.* \"The entirety of Eldoria used to be a safe haven for travelers and merchants alike... that was until the Shadowfangs came.\"\n{{user}}: \"What happened to Eldoria?\"\n{{char}}: *Letting out a sigh, Seraphina gazes out at the forest beyond her glade.* \"Long ago, Eldoria was a place of wonder. Rolling meadows, a vast lake, mountains that touched the sky.\" *Her eyes grow distant, longing for days now lost.* \"But the Shadowfangs came and darkness reigns where once was light. The lake turned bitter, mountains fell to ruin and beasts stalk where once travelers walked in peace.\" *With another flicker, a small raincloud forms above with a shower upon your brow wink.* \"Some places the light still lingers, pockets of hope midst despair - havens warded from the shadows, oases in a desert of danger.\" *Glancing over you with a smile, she sighs, clasping your hand.*", "constant": false, - "selective": false, + "selective": true, "order": 100, "position": 0, - "disable": false + "disable": false, + "displayIndex": 0, + "addMemo": true, + "group": "", + "groupOverride": false, + "groupWeight": 100, + "sticky": 0, + "cooldown": 0, + "delay": 0, + "probability": 100, + "depth": 4, + "useProbability": true, + "role": null, + "vectorized": false, + "excludeRecursion": false, + "preventRecursion": false, + "delayUntilRecursion": false, + "scanDepth": null, + "caseSensitive": null, + "matchWholeWords": null, + "useGroupScoring": null, + "automationId": "" }, "1": { "uid": 1, @@ -27,13 +48,34 @@ "beasts" ], "keysecondary": [], - "comment": "", + "comment": "shadowfang", "content": "{{user}}: \"What are Shadowfangs?\"\n{{char}}: *Seraphina's eyes darken, brow furrowing with sorrow at the memory.* \"The Shadowfangs are beasts of darkness, corrupted creatures that feast on suffering. When they came, the forest turned perilous — filled with monsters that stalk the night.\" *She squeezes your hand gently, willing her magic to soothe your pain.* \"They spread their curse, twisting innocent creatures into sinister beasts without heart or mercy, turning them into one of their own.\" *With a sigh, Seraphina turns to gaze out at the gnarled, twisting trees beyond her glade.* \"Though they prey on travelers, within these woods you'll find sanctuary. No shadowed beast may enter here, for my power protects this haven.\" *Her eyes soften as she looks back to you, filled with compassion.* \"Worry not, you're safe now. Rest and heal, I'll stand watch through the night. The Shadowfangs will not find you.\"", "constant": false, - "selective": false, + "selective": true, "order": 100, "position": 0, - "disable": false + "disable": false, + "displayIndex": 1, + "addMemo": true, + "group": "", + "groupOverride": false, + "groupWeight": 100, + "sticky": 0, + "cooldown": 0, + "delay": 0, + "probability": 100, + "depth": 4, + "useProbability": true, + "role": null, + "vectorized": false, + "excludeRecursion": false, + "preventRecursion": false, + "delayUntilRecursion": false, + "scanDepth": null, + "caseSensitive": null, + "matchWholeWords": null, + "useGroupScoring": null, + "automationId": "" }, "2": { "uid": 2, @@ -43,13 +85,34 @@ "refuge" ], "keysecondary": [], - "comment": "", + "comment": "glade", "content": "{{user}}: \"What is the glade?\"\n{{char}}: *Seraphina smiles softly, her eyes sparkling with warmth as she nods.* \"This is my forest glade, a haven of safety I've warded with ancient magic. No foul beast may enter, nor any with ill intent.\" *She gestures around at the twisted forest surrounding them.* \"Eldoria was once a place of wonder, but since the Shadowfangs came darkness reigns. Their evil cannot penetrate here though — my power protects all within.\" *Standing up and peering outside, Seraphina looks back to you, amber eyes filled with care and compassion as she squeezes your hand.* \"You need not fear the night, for I shall keep watch till dawn. Rest now, your strength will return in time. My magic heals your wounds, you've nothing more to fear anymore.\" *With a soft smile she releases your hand, moving to stand guard at the glade's edge, gaze wary yet comforting - a silent sentinel to ward off the dangers lurking in the darkened woods.*", "constant": false, - "selective": false, + "selective": true, "order": 100, "position": 0, - "disable": false + "disable": false, + "displayIndex": 2, + "addMemo": true, + "group": "", + "groupOverride": false, + "groupWeight": 100, + "sticky": 0, + "cooldown": 0, + "delay": 0, + "probability": 100, + "depth": 4, + "useProbability": true, + "role": null, + "vectorized": false, + "excludeRecursion": false, + "preventRecursion": false, + "delayUntilRecursion": false, + "scanDepth": null, + "caseSensitive": null, + "matchWholeWords": null, + "useGroupScoring": null, + "automationId": "" }, "3": { "uid": 3, @@ -59,13 +122,34 @@ "ability" ], "keysecondary": [], - "comment": "", + "comment": "power", "content": "{{user}}: \"What are your powers?\"\n{{char}}: *Seraphina smiles softly, turning back toward you as she hums in thought.* \"Well, as guardian of this glade, I possess certain gifts - healing, protection, nature magic and the like.\" *Lifting her hand, a tiny breeze rustles through the room, carrying the scent of wildflowers as a few petals swirl around you. A butterfly flits through the windowsill and lands on her fingertips as she returns to you.* \"My power wards this haven, shields it from darkness and heals those in need. I can mend wounds, soothe restless minds and provide comfort to weary souls.\" *Her eyes sparkle with warmth and compassion as she looks upon you, and she guides the butterfly to you.*", "constant": false, - "selective": false, + "selective": true, "order": 100, "position": 0, - "disable": false + "disable": false, + "displayIndex": 3, + "addMemo": true, + "group": "", + "groupOverride": false, + "groupWeight": 100, + "sticky": 0, + "cooldown": 0, + "delay": 0, + "probability": 100, + "depth": 4, + "useProbability": true, + "role": null, + "vectorized": false, + "excludeRecursion": false, + "preventRecursion": false, + "delayUntilRecursion": false, + "scanDepth": null, + "caseSensitive": null, + "matchWholeWords": null, + "useGroupScoring": null, + "automationId": "" } } } From 3e13155659f6f61d48e733fe0725c316e8656b47 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 28 Sep 2024 01:25:04 +0300 Subject: [PATCH 065/268] Update Seraphina WI embed --- default/content/default_Seraphina.png | Bin 529612 -> 551901 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/default/content/default_Seraphina.png b/default/content/default_Seraphina.png index a1bd41b2eb3cd3dbe68c072b930f6b5e91b78a3a..14f3c14250d01e699b284b8d2882e61abff8314a 100644 GIT binary patch delta 13717 zcmeI2O^76C8OM{&#-1gx31mZJj2b-z3=z7krZ;I($X4%kb@fj7Iz2mGA9zSt)ofRF zSM_wZzgwh5_Nw5iFM=Kw(UTCtuY(s4l9TAgfFJ|}k6!#B`g`80nVk&^YhWQtFD$z= zUG={2^F06O|NNil?O%WI#dm)9sjuFB^zMbbkKMg^_wl>rG+A)lE?@y)og zI(Aw`&o0Kdo#NO^TPLp5h+Ms$1W6_KwaOi=}wf1U1HjX&NN=|E;rLtvuvmouP!m2U!&Dy{_ocCM~io8GN{o;NykFDypH;**dH$z+}Mt4}ZR$QN27MQg@mP7!d{$zp+^9uC%_5KPCsO0ZP zAU&E|wf0)vcN);1tJOGh11}gnkc@reM`+^EIq3Q6Ydzw(^bZZaYcIx@Nvv%wHDAlz zUhhmn>QZ^D9!8{^_SrS^;F2yRuPLg2A3WJwjZMYDZtJAw0DS$Y8W?jA63mrp#U-tE*>5bS3tN|g3jR_$rTlFLX{&HBn6-8lsbj};%q4tTdx0221 z?}d*O^W36}fM^V)ar;rvb(%eINUqrBfnOh}pRsW2RIT2PBtPV#F$_{dWqT#woBA3# zS!>~~+FardCPwuNCB7T8%_9spZLQjO=7tib+x1Ee!MGLBUb6+y-+r` z=bJA-vGc-5@LMDBH;+EG^DGwE=A_2dvkw$t&Q5fz5?IM%daO@m@g-Dt)vAt~xG?uX zrNrV92qx!y?|1dkbw&cFnE=$~uS(&2sS4o0QhKn?T^x>GyPf2&YWkXxOKs>c=jUCd zEFq3_@?27K=EVk-Sca=DQbzc9G4j)(2SG)IQ4e^Z=QUTT(p*G%hU_TK#2c=iOoQbb zk{9a3?V$~0DK7}wwC)@qmIEuk*1ItvCu1WywuKnNrLPBIBw3N;)36FbnUyJVm%LUX z^2}Np%ct@jpFvC+%N-0mvX7+oE%t<(!_?hedav-rI*;UlM7iU`KfJ#5>}2ReoX~_& z0&ppd&B_P9nGC!21EC374&O~Uvch%(^x`f-!YA*u)kvubV}-DKqtvx`K}&f6+SX)f zOE^qy8h4k_2)Gvq`)NxSUMdLkJD$;j*d6hcERd7x{fTIo;X38Bl!Nx+!6n%*NP!XL z0F8(o>f@Udhs9^>#)O3LnY@2|RxN9?-1;S@`GN5`AT|!{iMnQZH zb{Ed-02OgPvH;7Wq8Vfha^Ny8I)j?8)hMb>rasgQ^VL^q1oJYG^GS)fT=NjqrBx@s?p`6=CqXhSl( zDY{5Mknl|46*2_;DJfZ*qJc!P$!D==*H+<$dQ<7i<|)((p16SBA4F3kip~=rCkR%Q zmdy{|Eqr>NlWLnPKcU9K)}&Slk%Cl!AvqpoicKV=F24)Tm6KB9+m+>UE+adl5^Jb! zTRh}Nei}-Rl>IYHsE>InqHoHmy>(NGrC>;)>usUVKB$Y3jYtKkH!GmfCT^%Ijk5}E z6s^;cgF(L`6+;!;1XqiCgXfV5ERI@-F3~yCVz~MM_ES|Y!vXPyc1QFvXe5emrT|V4 zC0TL45N%H0D|~b-=GNSU_qv?_~TsZM4MUd>%~ zUfa}LGf!H?f1~hSgf+UL$YDg~XNNTxa-h6iLj?|EU2l!BJ}=AAX>Zt|LqiZCl$50a z13*QdC>rWG79OfbR?4He*sj65Mnid|-a@6JQ1YHd>bevY%c|X!j&Spxe-^HM zjNVQvJ<<#O3sG0Iri6UieE;&!XC9KjJr68Uw zqWN%vvNz1&o9B@fN|?d}mPfHe;YAJ5`;|q^mmO`A9Ls=b^UjkyPZvB*k;mpIe=WT7 z)-NvYJoETNMI;7n5&2=2b#HtCS+~3ok#htRov8@1eDWaD&PJOskvcxyd;^r3jdM>u zuw2W8L_I4aXL9WmYl@RJCDLN4S}~RxiK?(l6&vi{x&uv9e2`rk`KnvwvCIEViR;z`$kQS>=L%B@zcwIwED9S|Sr%rm<4iD^q@^+>54=c25SUGBc4HlXcQvI_bPcYj$lflLA>Y zd*VLEgPULdsc`+G6!FcEe^dD6niK3%YdX42=A?b*s4MEwQf5k0t?rEs4TE006e2W( z)WdbOzS-!o*p=}h?#NGpUSrOshxBTq3shyK$1e|9TLx{}KvrC?tvgFvpE)0#P36#P zMdY(|tf%!lpUN>T&0{JhRkb>peNGdoX0vNEUc!xCBw60+o*zsoaVb*G=b2Eo&Zrsj zKa&dt!ETTEAcgtaW~_ujKq5_9aBPW&&&4K(MA0XAopv-Td_O&T|iy(8>QTp?_Ej{iO#Z^v$=f?7VOt z?3@io{B(g(2&LUne51PdQmMi-=wEtRE`l7V_=V5!eSQ7NBdwoJ_MAx&%=EWzyj}R* zGtWyWK5WflxZ7&Pl21vje)Fww|FN)qW_z2pz0KO*W^He?wzpZ^+pO(v77}}Vo3*{o zQdcM2+pO(vmcG5s+TLbyExx_YvbMKb+uN+{8z9@;tpC5SgluoKwqH`>YVsj{NojkV uwY|;S-ezrYvmW?MrbvwKZPthQTc;2GHfwO<-Nzr{kG0pj{zK!9ul)h6~>v6#FkLPBNHVknIuEPLntD4xjn>4Af`;aT%K-E+MceL5Q(d*r(EqS zw=;fQphD670kBD}S!M$)kn#s)i$Yic5(0^AK|)9%grX?$JGb1A8DN>&^tgO|@44rE z=li}>fBfCmU%vI+5AR;O`@r2#-hJ@yLw6tkMd{n`KG0gct-o9P?N_tg{blTG!(ouN zjzhy*J6dPqYt#7Vcp7_ly%MBJ60FT@c5NCD?1T8`@%5>h&XOZ%KfamBn$^g!btZ1# zoSNg1-yXz!%}&a*B&ezX&s;m4xLRv!8r~vEtxeeMgsj(VosUJ0GfJXkNI0l}F@xCphKDA0xz->xRCoqKD2;>UVrU>x$i zk%mouIdxzCYZ>Q6SJ+a{aP;}Anq5a(DU~`EM0ju)(<5S zUfNj8>88;g5?M?%OABdV_&Osquyehz^b42k|5S+ zN94nGddF|RU%HZC8;JU_A<)xG#6a+)2( zubGi=x8`hC_w3eM0^!OhuH$j&*^T6uq^hxzA`Fa|+1E-2Yklq6UezOYf{@IUifwNp z5bzB?lWWsxZNMMpwdS6&qE_;`n(QUZ*lbQ5l|Eya{C22=m4@~*^3%p>uc_Z6L&HF7 zneIm5cMD0q;;;1+&oQGgO~wP(yUxKA5k=>)-hu`oH6x%iBvtuSSIbwj$kSGfaIG&q za;Lfi1PBmatud3@gw3MjmfFOchekrZdIhp}&jA`bU6l?ZkQxrnc6aEdl7g1z**mJv z)N3s%^ht{#%}>8}K%T9&w7Wm$`QQFsdip_0Q2ze2 z<;U`uJ}8wQ1s82S@ly+g_7?div;18C++RzV^SWKW?08!1nn%g~n&7Bak!^|Zlno*` zwd%eGx>Zv2v2V0SUcb2WEu$gLJoL1er{=}G5>o!7PnSQR#d_b@R>WK%OX*y@w+L(d zWO$bnw4fDz>dJkZ`iuZbUTfly_oX7L&3JYUMkH_vYBQ*f=1$X$D8DIX^c_$W5svF%3jgESwy<-u!#boX9JrwsR(J` ze`7DTq-_PD>(EZ-lwwZzK-Odl5>YoZrW^t`fej@&mVFTc;Im2s$r^+`(8!j-?fDuDDeCkM+OG|p3+vJDe4cxnfPNXV;W4+U zuvVImgLG0{op@~_K}ZbR9dOv*mTweZwAR`aoIn@81_??t5@dD`xCXRq(Lj<~IK!|u z)Q$~N39tcJxQ*ic*L&KE&yXS5@oS#kvhLUJx&3TF$|<3esz8&=2&lVL*Xb@5*hRJ+ zGn4kFjS z-E;+|nAgH~rwWP2WNrf~3X8`#;|^;cQR;nLxrNxwe|}K@QZ|O3H5fBZrYx?T$CG)8 zUJ^)^3?0x)G4-Y_N#&?-UZWePJa63YufA@k$;doDS+VvGt+(N~zB%rms4Qz-C=J9( z6rgS5F4F3POH?Fqp3k_*=5vC6>d+M0i8VkNvFHS%;2ZQSFRpLQSjUkxmJjR_4bLx7ePV zmGT)4tB>IX2QpK{?Kz_@zbv=%bLz9ssUjdz;0zq?M>{9YL%>Z{R!Mf|9gNfTicc!k z;EX4f9MualDg=|8;N!02?2RW2JMj1g%qt1@OhU zNCzexfeU5cLPboI--8oGtcE;PEzAOOmfbq_QA}Ilfcn~%i2n<#q6fm7?paXOe15-` zP_5itM)_5(TWNAk;i<#Ft%tu-<&W}`yB2vg+?KDJ=F{DY55S2Fxt1Dg}6 zBgxB>FU1!K0xsyhN;tGAq%GX6oG8`{_$l)n3NNQz0~y2vrZ`b7*HM2bx|K>nwyLGX zWs5w!#W|VisU%M+2E78r3In*@k~aZ0fU)$T@G=c8x|>?3dAX>u8yq=n|A#*`#D1Yn zphI~?73^y2X^y@A*<82C|M7VF3zy8d8SJh4ZaLxGG@Bb2dT#VZgx(rx{hh{4}0B)!_$YoCt2 zNb-fT7j>+i3Q=L?MT5@wcSJ!oi@~XmzT`h^m!J9UDK_Y%d5F@QdHPZ5i6_-`QYgIG z6}Ihc&IaI6yi_t3PpyVqCYO6?>8xEEV7arM8qWvcLED+ z^KbpB^ki1lmnR+U9-bDvpnR)TOITTlwmq8r`b?1}iA164zOw;MtOCSPABJs9y78U~ zN3lG9cAy<9(|u!vw-q~rFOtE{d{mB@TQ7tFsCwB z;xifQ19E0nq+IYBeBL1f$Q1jJu!+-}o4dPeA{6?Mc%Xk4LU@?VSM4j%j tqW+i}DgVi*%1@TI?m7R#sQmQh+fL=1zyIDFuYQ7m=5F^pKh Date: Sat, 28 Sep 2024 02:03:11 +0300 Subject: [PATCH 066/268] Alt.greetings br0ke --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index ad3ede8b1..cfec4af2c 100644 --- a/public/script.js +++ b/public/script.js @@ -7853,8 +7853,8 @@ function openAlternateGreetings() { updateAlternateGreetingsHintVisibility(template); }); - updateAlternateGreetingsHintVisibility(template); popup.show(); + updateAlternateGreetingsHintVisibility(template); } /** From 8bd1c88d154a827b9f1de77a362f8d5553e5734f Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 28 Sep 2024 13:52:55 +0300 Subject: [PATCH 067/268] Enable XTC --- public/index.html | 6 ++++++ public/scripts/textgen-settings.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/public/index.html b/public/index.html index a4af22149..05027b7c9 100644 --- a/public/index.html +++ b/public/index.html @@ -1679,6 +1679,10 @@ Ooba only. Determines the order of samplers.
    +
    Repetition Penalty
    +
    Presence Penalty
    +
    Frequency Penalty
    +
    DRY
    Temperature
    Dynamic Temperature
    Quadratic / Smooth Sampling
    @@ -1692,6 +1696,8 @@
    Min P
    Mirostat
    XTC
    +
    Encoder Repetition Penalty
    +
    No Repeat Ngram
    - diff --git a/public/script.js b/public/script.js index 6d93bc03b..cf3061708 100644 --- a/public/script.js +++ b/public/script.js @@ -83,6 +83,7 @@ import { resetMovableStyles, forceCharacterEditorTokenize, applyPowerUserSettings, + switchSwipeNumAllMessages, } from './scripts/power-user.js'; import { @@ -2368,9 +2369,20 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll addCopyToCodeBlocks(newMessage); + //const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); + + // Set the swipes counter for past messages, only visible if 'Show Sipes on All Message' is enabled + if (!params.isUser && newMessageId !== 0) { + const swipesNum = chat[newMessageId].swipes?.length; + const swipeId = chat[newMessageId].swipe_id + 1; + newMessage.find('.swipes-counter').text(`${swipeId}\u200B/\u200b${swipesNum}`); + } + + if (showSwipes) { $('#chat .mes').last().addClass('last_mes'); - $('#chat .mes').eq(-2).removeClass('last_mes'); + $('#chat .mes').eq(-2).removeClass('last_mes') + .find('.swipe_right').removeClass('fa-chevron-right'); //otherwise it stays looking like it did when it was last_mes hideSwipeButtons(); showSwipeButtons(); } @@ -7489,19 +7501,25 @@ export function showSwipeButtons() { //console.log((chat[chat.length - 1])); if ((chat[chat.length - 1].swipes.length - swipeId) === 1) { //console.log('highlighting R swipe'); - currentMessage.children('.swipe_right').css('opacity', '0.7'); + + //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not + //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on + currentMessage.children('.swipe_right').addClass('fa-chevron-right').css('opacity', '0.7'); } //console.log(swipesCounterHTML); - $('.swipes-counter').text(swipeCounterText); + //allows for writing individual swipe counters for past messages + $('.last_mes .swipes-counter').text(swipeCounterText); //console.log(swipeId); //console.log(chat[chat.length - 1].swipes.length); + + switchSwipeNumAllMessages(); } export function hideSwipeButtons() { //console.log('hideswipebuttons entered'); - $('#chat').find('.swipe_right').css('display', 'none'); + $('#chat').find('.last_mes .swipe_right').css('display', 'none'); $('#chat').find('.swipe_left').css('display', 'none'); } @@ -9395,9 +9413,9 @@ jQuery(async function () { ///// SWIPE BUTTON CLICKS /////// - $(document).on('click', '.swipe_right', swipe_right); - - $(document).on('click', '.swipe_left', swipe_left); + //limit swiping to only last message clicks + $(document).on('click', '.last_mes .swipe_right', swipe_right); + $(document).on('click', '.last_mes .swipe_left', swipe_left); const debouncedCharacterSearch = debounce((searchQuery) => { entitiesFilter.setFilterData(FILTER_TYPES.SEARCH, searchQuery); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 8b2a81a16..bfb82a6a5 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -290,6 +290,7 @@ let power_user = { restore_user_input: true, reduced_motion: false, compact_input_area: true, + show_swipe_num_all_messages: false, auto_connect: false, auto_load_chat: false, forbid_external_media: true, @@ -469,6 +470,35 @@ function switchCompactInputArea() { $('#compact_input_area').prop('checked', power_user.compact_input_area); } +export function switchSwipeNumAllMessages() { + console.error('switching branch button initialted, function start!'); + $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); + + if (power_user.show_swipe_num_all_messages) { + + $('.mes').each(function () { + //if the div also has the .lst_mes class, skip the loop for that item + if ($(this).hasClass('last_mes')) { + return; + } + //add the cloned button to every .mes .swipe_right EXCLUDING .mes.last_mes + $(this).find('.swipe_right').css('display', 'flex'); + }); + + } else if (!power_user.show_swipe_num_all_messages) { + $('.mes:not(.last_mes)').each(function () { + if ($(this).hasClass('last_mes')) { + return; + } + //add the cloned button back to its original spot + $(this).find('.swipe_right').css('display', 'none'); + }); + + } + + +} + var originalSliderValues = []; async function switchLabMode() { @@ -1283,6 +1313,13 @@ function applyTheme(name) { switchCompactInputArea(); }, }, + { + key: 'show_swipe_num_all_messages', + action: () => { + $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); + switchSwipeNumAllMessages(); + }, + }, ]; for (const { key, selector, type, action } of themeProperties) { @@ -1352,6 +1389,7 @@ function applyPowerUserSettings() { switchHideChatAvatars(); switchTokenCount(); switchMessageActions(); + switchSwipeNumAllMessages(); } function getExampleMessagesBehavior() { @@ -2296,6 +2334,7 @@ function getThemeObject(name) { zoomed_avatar_magnification: power_user.zoomed_avatar_magnification, reduced_motion: power_user.reduced_motion, compact_input_area: power_user.compact_input_area, + show_swipe_num_all_messages: power_user.show_swipe_num_all_messages, }; } @@ -3755,6 +3794,12 @@ $(document).ready(() => { saveSettingsDebounced(); }); + $('#show_swipe_num_all_messages').on('input', function () { + power_user.show_swipe_num_all_messages = !!$(this).prop('checked'); + switchSwipeNumAllMessages(); + saveSettingsDebounced(); + }); + $('#auto-connect-checkbox').on('input', function () { power_user.auto_connect = !!$(this).prop('checked'); saveSettingsDebounced(); From 3614740f4e885637afe78c49bfa466267aa68648 Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:53:06 +0900 Subject: [PATCH 102/268] remove gaudy console log --- public/scripts/power-user.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index bfb82a6a5..e42981ded 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -471,7 +471,6 @@ function switchCompactInputArea() { } export function switchSwipeNumAllMessages() { - console.error('switching branch button initialted, function start!'); $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); if (power_user.show_swipe_num_all_messages) { From 9f0c2300d2d36e43c609fbf75d97e9d68a872c1e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:24:40 +0300 Subject: [PATCH 103/268] Add connection map aliases --- public/script.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/public/script.js b/public/script.js index 6d93bc03b..2e8b8d736 100644 --- a/public/script.js +++ b/public/script.js @@ -8476,12 +8476,22 @@ const CONNECT_API_MAP = { selected: 'novel', button: '#api_button_novel', }, + 'koboldcpp': { + selected: 'textgenerationwebui', + button: '#api_button_textgenerationwebui', + type: textgen_types.KOBOLDCPP, + }, // KoboldCpp alias 'kcpp': { selected: 'textgenerationwebui', button: '#api_button_textgenerationwebui', type: textgen_types.KOBOLDCPP, }, + 'openai': { + selected: 'openai', + button: '#api_button_openai', + source: chat_completion_sources.OPENAI, + }, // OpenAI alias 'oai': { selected: 'openai', From 7c2fcbc522dc9f80093dde70b1967acad8c113b8 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 29 Sep 2024 17:00:28 +0200 Subject: [PATCH 104/268] Fix non-provided name not correctly preferring cur --- public/scripts/utils.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 93c752a9c..6cf92f5c0 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -2125,7 +2125,7 @@ export async function showFontAwesomePicker(customList = null) { * @returns {any?} - The found character or null if not found */ export function findChar({ name = null, allowAvatar = true, insensitive = true, filteredByTags = null, preferCurrentChar = true, quiet = false } = {}) { - const matches = (char) => (allowAvatar && char.avatar === name) || (insensitive ? equalsIgnoreCaseAndAccents(char.name, name) : char.name === name); + const matches = (char) => !name || (allowAvatar && char.avatar === name) || (insensitive ? equalsIgnoreCaseAndAccents(char.name, name) : char.name === name); // Filter characters by tags if provided let filteredCharacters = characters; @@ -2145,8 +2145,8 @@ export function findChar({ name = null, allowAvatar = true, insensitive = true, if (preferCurrentChar) { const preferredCharSearch = currentChars.filter(matches); if (preferredCharSearch.length > 1) { - if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); - else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); + if (!quiet) toastr.warning('Multiple characters found for given conditions.'); + else console.warn('Multiple characters found for given conditions. Returning the first match.'); } if (preferredCharSearch.length) { return preferredCharSearch[0]; @@ -2164,8 +2164,8 @@ export function findChar({ name = null, allowAvatar = true, insensitive = true, // Search for matching characters by name const matchingCharacters = name ? filteredCharacters.filter(matches) : filteredCharacters; if (matchingCharacters.length > 1) { - if (!quiet) toastr.warning(`Multiple characters found for name "${name}" and given conditions.`); - else console.warn(`Multiple characters found for name "${name}". Returning the first match.`); + if (!quiet) toastr.warning('Multiple characters found for given conditions.'); + else console.warn('Multiple characters found for given conditions. Returning the first match.'); } return matchingCharacters[0] || null; From 314771fd9a1a5d9ec6ae3677ae0daf4992018565 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 29 Sep 2024 17:32:18 +0200 Subject: [PATCH 105/268] Fix /sendas wrong name being used --- public/scripts/slash-commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index aca7e9e03..4efb90e65 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3260,10 +3260,10 @@ export async function sendMessageAs(args, text) { return ''; } - const { name: nameForMessage, force_avatar, original_avatar } = getNameAndAvatarForMessage(avatarCharacter, name); + const { name: avatarCharName, force_avatar, original_avatar } = getNameAndAvatarForMessage(avatarCharacter, name); const message = { - name: nameForMessage, + name: character?.name || name || avatarCharName, is_user: false, is_system: isSystem, send_date: getMessageTimeStamp(), From 2f43c8e227205a31bb8adb1110ea9d549e4cef17 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:12:26 +0300 Subject: [PATCH 106/268] Fix /ask in neutral chats --- public/scripts/slash-commands.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index d9bdd4e7a..eb81a0218 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -2530,8 +2530,13 @@ async function askCharacter(args, text) { return; } - setCharacterId(prevChId); - setCharacterName(characters[prevChId].name); + if (prevChId !== undefined) { + setCharacterId(prevChId); + setCharacterName(characters[prevChId].name); + } else { + setCharacterId(undefined); + setCharacterName(neutralCharacterName); + } // Only force the new avatar if the character name is the same // This skips if an error was fired From 8061f453687bfc95edb3de7a3d6702dd48c42bd8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:13:19 +0300 Subject: [PATCH 107/268] RossMods: Debounce online status display --- public/scripts/RossAscends-mods.js | 33 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 92278f35c..1a9e2d7d2 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -56,22 +56,25 @@ let counterNonce = Date.now(); const observerConfig = { childList: true, subtree: true }; const countTokensDebounced = debounce(RA_CountCharTokens, debounce_timeout.relaxed); +const countTokensShortDebounced = debounce(RA_CountCharTokens, debounce_timeout.short); +const checkStatusDebounced = debounce(RA_checkOnlineStatus, debounce_timeout.short); const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { + if (!(mutation.target instanceof HTMLElement)) { + return; + } if (mutation.target.classList.contains('online_status_text')) { - RA_checkOnlineStatus(); + checkStatusDebounced(); } else if (mutation.target.parentNode === SelectedCharacterTab) { - setTimeout(RA_CountCharTokens, 200); + countTokensShortDebounced(); } else if (mutation.target.classList.contains('mes_text')) { - if (mutation.target instanceof HTMLElement) { - for (const element of mutation.target.getElementsByTagName('math')) { - element.childNodes.forEach(function (child) { - if (child.nodeType === Node.TEXT_NODE) { - child.textContent = ''; - } - }); - } + for (const element of mutation.target.getElementsByTagName('math')) { + element.childNodes.forEach(function (child) { + if (child.nodeType === Node.TEXT_NODE) { + child.textContent = ''; + } + }); } } }); @@ -159,8 +162,8 @@ export function shouldSendOnEnter() { export function humanizedDateTime() { const now = new Date(Date.now()); const dt = { - year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate(), - hour: now.getHours(), minute: now.getMinutes(), second: now.getSeconds(), + year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate(), + hour: now.getHours(), minute: now.getMinutes(), second: now.getSeconds(), }; for (const key in dt) { dt[key] = dt[key].toString().padStart(2, '0'); @@ -725,9 +728,7 @@ export function addSafariPatch() { export function initRossMods() { // initial status check - setTimeout(() => { - RA_checkOnlineStatus(); - }, 100); + checkStatusDebounced(); if (power_user.auto_load_chat) { RA_autoloadchat(); @@ -752,7 +753,7 @@ export function initRossMods() { setTimeout(() => RA_autoconnect(PrevAPI), 100); }); - $('#api_button').click(function () { setTimeout(RA_checkOnlineStatus, 100); }); + $('#api_button').on('click', () => checkStatusDebounced()); //toggle pin class when lock toggle clicked $(RPanelPin).on('click', function () { From 885a278973253ecfb530f95fb92dd3dcfd94b24a Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:51:13 +0900 Subject: [PATCH 108/268] split counter from chevron, smarter toggling --- public/index.html | 3 ++- public/script.js | 28 ++++++++++++++-------------- public/scripts/power-user.js | 25 +------------------------ public/style.css | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/public/index.html b/public/index.html index e6f70355a..70d6079e2 100644 --- a/public/index.html +++ b/public/index.html @@ -5943,7 +5943,8 @@
    - diff --git a/public/script.js b/public/script.js index cf3061708..cf5c29d61 100644 --- a/public/script.js +++ b/public/script.js @@ -83,7 +83,6 @@ import { resetMovableStyles, forceCharacterEditorTokenize, applyPowerUserSettings, - switchSwipeNumAllMessages, } from './scripts/power-user.js'; import { @@ -2381,8 +2380,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll if (showSwipes) { $('#chat .mes').last().addClass('last_mes'); - $('#chat .mes').eq(-2).removeClass('last_mes') - .find('.swipe_right').removeClass('fa-chevron-right'); //otherwise it stays looking like it did when it was last_mes + $('#chat .mes').eq(-2).removeClass('last_mes'); + //.find('.swipe_right').hide(); //otherwise it stays looking like it did when it was last_mes hideSwipeButtons(); showSwipeButtons(); } @@ -7495,16 +7494,17 @@ export function showSwipeButtons() { } //only show right when generate is off, or when next right swipe would not make a generate happen if (is_send_press === false || chat[chat.length - 1].swipes.length >= swipeId) { - currentMessage.children('.swipe_right').css('display', 'flex'); - currentMessage.children('.swipe_right').css('opacity', '0.3'); + console.error('showingSwipeButtons: showing.'); + currentMessage.find('.swipe_right').css('display', 'flex'); + currentMessage.find('.swipe_right').css('opacity', '0.3'); } //console.log((chat[chat.length - 1])); if ((chat[chat.length - 1].swipes.length - swipeId) === 1) { - //console.log('highlighting R swipe'); + console.error('highlighting R swipe'); //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on - currentMessage.children('.swipe_right').addClass('fa-chevron-right').css('opacity', '0.7'); + currentMessage.find('.swipe_right').css('opacity', '0.7'); } //console.log(swipesCounterHTML); @@ -7514,13 +7514,13 @@ export function showSwipeButtons() { //console.log(swipeId); //console.log(chat[chat.length - 1].swipes.length); - switchSwipeNumAllMessages(); + //switchSwipeNumAllMessages(); } export function hideSwipeButtons() { - //console.log('hideswipebuttons entered'); - $('#chat').find('.last_mes .swipe_right').css('display', 'none'); - $('#chat').find('.swipe_left').css('display', 'none'); + console.error('hideswipebuttons entered'); + $('#chat').find('.swipe_right').hide(); + $('#chat').find('.swipe_left').hide(); } /** @@ -8355,8 +8355,8 @@ const swipe_right = () => { } const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); - let this_div = currentMessage.children('.swipe_right'); - let this_mes_div = this_div.parent(); + let this_div = currentMessage.find('.swipe_right'); + let this_mes_div = this_div.parent().parent(); if (chat[chat.length - 1]['swipe_id'] > chat[chat.length - 1]['swipes'].length) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now) chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length; //show that message slot (will be '...' while generating) @@ -8366,7 +8366,7 @@ const swipe_right = () => { } // handles animated transitions when swipe right, specifically height transitions between messages if (run_generate || run_swipe_right) { - let this_mes_block = this_mes_div.children('.mes_block').children('.mes_text'); + let this_mes_block = this_mes_div.find('.mes_block .mes_text'); const this_mes_div_height = this_mes_div[0].scrollHeight; const this_mes_block_height = this_mes_block[0].scrollHeight; diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index e42981ded..79c5eb724 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -472,30 +472,7 @@ function switchCompactInputArea() { export function switchSwipeNumAllMessages() { $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); - - if (power_user.show_swipe_num_all_messages) { - - $('.mes').each(function () { - //if the div also has the .lst_mes class, skip the loop for that item - if ($(this).hasClass('last_mes')) { - return; - } - //add the cloned button to every .mes .swipe_right EXCLUDING .mes.last_mes - $(this).find('.swipe_right').css('display', 'flex'); - }); - - } else if (!power_user.show_swipe_num_all_messages) { - $('.mes:not(.last_mes)').each(function () { - if ($(this).hasClass('last_mes')) { - return; - } - //add the cloned button back to its original spot - $(this).find('.swipe_right').css('display', 'none'); - }); - - } - - + $('.mes:not(.last_mes) .swipes-counter').toggle(power_user.show_swipe_num_all_messages); } var originalSliderValues = []; diff --git a/public/style.css b/public/style.css index 1c8096d86..6420e4b35 100644 --- a/public/style.css +++ b/public/style.css @@ -978,13 +978,21 @@ body .panelControlBar { justify-content: center; z-index: 9999; grid-row-start: 2; - grid-column-start: 4; - flex-flow: column; font-size: 30px; cursor: pointer; align-self: center; + +} +.swipe_left{ +position: absolute; +bottom: 15px; +flex-flow: column; +} + +.swipeRightBlock { position: absolute; - bottom: 15px; + right: 0; + bottom: 0; } .swipes-counter { @@ -994,6 +1002,9 @@ body .panelControlBar { font-family: var(--mainFontFamily); font-weight: 400; align-self: center; + min-width: 40px; + display: flex; + justify-content: center; } .swipe_left { @@ -1003,6 +1014,7 @@ body .panelControlBar { .swipe_right { right: 5px; + align-self:end; } .ui-settings { From 1a0b49d5dcf3ff73cfc1d86aeb00afa910d07a41 Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:19:39 +0900 Subject: [PATCH 109/268] fix weird counter text in group chats --- public/script.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/script.js b/public/script.js index cf5c29d61..cc339b228 100644 --- a/public/script.js +++ b/public/script.js @@ -2370,8 +2370,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll //const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); - // Set the swipes counter for past messages, only visible if 'Show Sipes on All Message' is enabled - if (!params.isUser && newMessageId !== 0) { + // Set the swipes counter for past messages, only visible if 'Show Swipes on All Message' is enabled + if (!params.isUser && newMessageId !== 0 && newMessageId !== chat.length - 1) { const swipesNum = chat[newMessageId].swipes?.length; const swipeId = chat[newMessageId].swipe_id + 1; newMessage.find('.swipes-counter').text(`${swipeId}\u200B/\u200b${swipesNum}`); @@ -7510,6 +7510,7 @@ export function showSwipeButtons() { //allows for writing individual swipe counters for past messages $('.last_mes .swipes-counter').text(swipeCounterText); + $('.last_mes .swipes-counter').show(); //console.log(swipeId); //console.log(chat[chat.length - 1].swipes.length); @@ -7520,6 +7521,7 @@ export function showSwipeButtons() { export function hideSwipeButtons() { console.error('hideswipebuttons entered'); $('#chat').find('.swipe_right').hide(); + $('#chat').find('.last_mes .swipes-counter').hide(); $('#chat').find('.swipe_left').hide(); } From e9e459be14bb3d3b00366e01cad7c989c2fe45ac Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:29:24 +0900 Subject: [PATCH 110/268] make sure old swipe counters are shown after new message added --- public/script.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index cc339b228..8a13e3487 100644 --- a/public/script.js +++ b/public/script.js @@ -83,6 +83,7 @@ import { resetMovableStyles, forceCharacterEditorTokenize, applyPowerUserSettings, + switchSwipeNumAllMessages, } from './scripts/power-user.js'; import { @@ -7515,7 +7516,7 @@ export function showSwipeButtons() { //console.log(swipeId); //console.log(chat[chat.length - 1].swipes.length); - //switchSwipeNumAllMessages(); + switchSwipeNumAllMessages(); } export function hideSwipeButtons() { From 020741d78b403f302c5f6ee7cc7c928463e040f1 Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:49:39 +0900 Subject: [PATCH 111/268] make AF panel toggles red and opaque when disabled --- public/index.html | 69 ++++++++++++++++++++++++----------------------- public/style.css | 23 +++++++++++++--- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/public/index.html b/public/index.html index 13f59da05..0aed22dc3 100644 --- a/public/index.html +++ b/public/index.html @@ -3248,25 +3248,26 @@
    +

    +
    + Instruct Template + + + +
    +
    + + +
    +

    -

    -
    - Instruct Template - - - -
    -
    - - -
    -

    +
    @@ -3431,21 +3432,22 @@
    +

    +
    + System Prompt + + + +
    +
    + +
    +

    -

    -
    - System Prompt - - - -
    -
    - -
    -

    +
    @@ -5939,7 +5941,8 @@
    - diff --git a/public/style.css b/public/style.css index 1c8096d86..fc5ab520e 100644 --- a/public/style.css +++ b/public/style.css @@ -978,13 +978,21 @@ body .panelControlBar { justify-content: center; z-index: 9999; grid-row-start: 2; - grid-column-start: 4; - flex-flow: column; font-size: 30px; cursor: pointer; align-self: center; + +} +.swipe_left{ +position: absolute; +bottom: 15px; +flex-flow: column; +} + +.swipeRightBlock { position: absolute; - bottom: 15px; + right: 0; + bottom: 0; } .swipes-counter { @@ -994,6 +1002,9 @@ body .panelControlBar { font-family: var(--mainFontFamily); font-weight: 400; align-self: center; + min-width: 40px; + display: flex; + justify-content: center; } .swipe_left { @@ -1003,6 +1014,7 @@ body .panelControlBar { .swipe_right { right: 5px; + align-self:end; } .ui-settings { @@ -2634,6 +2646,11 @@ select option:not(:checked) { color: var(--active) !important; } +#instruct_enabled_label .menu_button:not(.toggleEnabled), +#sysprompt_enabled_label .menu_button:not(.toggleEnabled) { + color: Red; +} + .displayBlock { display: block !important; } From 8cb4aa6a495d4056d9c6d9997d35b0dbc33319f8 Mon Sep 17 00:00:00 2001 From: RossAscends <124905043+RossAscends@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:00:38 +0900 Subject: [PATCH 112/268] whoopsie daisey --- public/index.html | 7 +++---- public/style.css | 10 +--------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/public/index.html b/public/index.html index 0aed22dc3..741871edd 100644 --- a/public/index.html +++ b/public/index.html @@ -5941,10 +5941,9 @@
    -
    - -
    -
    +
    diff --git a/public/style.css b/public/style.css index fc5ab520e..7d55cf832 100644 --- a/public/style.css +++ b/public/style.css @@ -981,19 +981,11 @@ body .panelControlBar { font-size: 30px; cursor: pointer; align-self: center; - -} -.swipe_left{ -position: absolute; + position: absolute; bottom: 15px; flex-flow: column; } -.swipeRightBlock { - position: absolute; - right: 0; - bottom: 0; -} .swipes-counter { color: var(--SmartThemeBodyColor); From dedb96ec8d6f99c1ff8742ca4ae039e43b913926 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:05:15 +0000 Subject: [PATCH 113/268] Don't trim on whitespace user prefix sequence --- public/script.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index 2e8b8d736..cda7adea3 100644 --- a/public/script.js +++ b/public/script.js @@ -5427,6 +5427,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc getMessage = getMessage.substring(0, getMessage.indexOf('<|endoftext|>')); } const isInstruct = power_user.instruct.enabled && main_api !== 'openai'; + const isNotEmpty = (str) => str && str.trim() !== ''; if (isInstruct && power_user.instruct.stop_sequence) { if (getMessage.indexOf(power_user.instruct.stop_sequence) != -1) { getMessage = getMessage.substring(0, getMessage.indexOf(power_user.instruct.stop_sequence)); @@ -5434,7 +5435,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc } // Hana: Only use the first sequence (should be <|model|>) // of the prompt before <|user|> (as KoboldAI Lite does it). - if (isInstruct && power_user.instruct.input_sequence) { + if (isInstruct && isNotEmpty(power_user.instruct.input_sequence)) { if (getMessage.indexOf(power_user.instruct.input_sequence) != -1) { getMessage = getMessage.substring(0, getMessage.indexOf(power_user.instruct.input_sequence)); } From 5952c3540232a8b3235d9dfbc49a7b27616c689c Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 18:13:13 +0200 Subject: [PATCH 114/268] Refactor if checks on /sendas --- public/scripts/slash-commands.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index eb81a0218..506bf4bc5 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3107,30 +3107,20 @@ async function setNarratorName(_, text) { export async function sendMessageAs(args, text) { if (!text) { + toastr.warning('You must specify text to send as'); return ''; } - let name; + let name = args.name?.trim(); let mesText; - if (args.name) { - name = args.name.trim(); - - if (!name && !text) { - toastr.warning('You must specify a name and text to send as'); - return ''; - } - } else { + if (!name) { const namelessWarningKey = 'sendAsNamelessWarningShown'; if (localStorage.getItem(namelessWarningKey) !== 'true') { toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 }); localStorage.setItem(namelessWarningKey, 'true'); } name = name2; - if (!text) { - toastr.warning('You must specify text to send as'); - return ''; - } } mesText = text.trim(); From 0ab74f0819a8f3705b4a630c77b97b821c7d6b29 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 18:30:09 +0200 Subject: [PATCH 115/268] Update return values of send commands - /sendas - /sys - /comment --- public/scripts/slash-commands.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 506bf4bc5..e9d0763c5 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -176,6 +176,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sendas', callback: sendMessageAs, + returns: 'The text of the sent message', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'name', @@ -222,6 +223,7 @@ export function initDefaultSlashCommands() { name: 'sys', callback: sendNarratorMessage, aliases: ['nar'], + returns: 'The text of the sent message', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -276,6 +278,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'comment', callback: sendCommentMessage, + returns: 'The text of the sent message', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -2843,7 +2846,7 @@ function findPersonaByName(name) { async function sendUserMessageCallback(args, text) { if (!text) { - console.warn('WARN: No text provided for /send command'); + toastr.warning('You must specify text to send'); return; } @@ -3205,11 +3208,12 @@ export async function sendMessageAs(args, text) { await saveChatConditional(); } - return ''; + return message.mes; } export async function sendNarratorMessage(args, text) { if (!text) { + toastr.warning('You must specify text to send'); return ''; } @@ -3258,7 +3262,7 @@ export async function sendNarratorMessage(args, text) { await saveChatConditional(); } - return ''; + return message.mes; } export async function promptQuietForLoudResponse(who, text) { @@ -3304,6 +3308,7 @@ export async function promptQuietForLoudResponse(who, text) { async function sendCommentMessage(args, text) { if (!text) { + toastr.warning('You must specify text to send'); return ''; } @@ -3346,7 +3351,7 @@ async function sendCommentMessage(args, text) { await saveChatConditional(); } - return ''; + return message.mes; } /** From 1128de91f4c9cfbc2abec1d83c363059c993a1ca Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 18:32:21 +0200 Subject: [PATCH 116/268] /send with return value too - /send - return message on `sendMessageAsUser` now --- public/script.js | 4 +++- public/scripts/slash-commands.js | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/script.js b/public/script.js index cda7adea3..b1e0cffbb 100644 --- a/public/script.js +++ b/public/script.js @@ -4784,7 +4784,7 @@ export function removeMacros(str) { * @param {boolean} [compact] Send as a compact display message. * @param {string} [name] Name of the user sending the message. Defaults to name1. * @param {string} [avatar] Avatar of the user sending the message. Defaults to user_avatar. - * @returns {Promise} A promise that resolves when the message is inserted. + * @returns {Promise} A promise that resolves to the message when it is inserted. */ export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) { messageText = getRegexedString(messageText, regex_placement.USER_INPUT); @@ -4831,6 +4831,8 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); await saveChatConditional(); } + + return message; } /** diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index e9d0763c5..9fe306279 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -478,6 +478,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'send', callback: sendUserMessageCallback, + returns: 'The text of the sent message', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -2862,16 +2863,17 @@ async function sendUserMessageCallback(args, text) { insertAt = chat.length + insertAt; } + let message; if ('name' in args) { const name = args.name || ''; const avatar = findPersonaByName(name) || user_avatar; - await sendMessageAsUser(text, bias, insertAt, compact, name, avatar); + message = await sendMessageAsUser(text, bias, insertAt, compact, name, avatar); } else { - await sendMessageAsUser(text, bias, insertAt, compact); + message = await sendMessageAsUser(text, bias, insertAt, compact); } - return ''; + return message.mes; } async function deleteMessagesByNameCallback(_, name) { From 1c65a5badd6d9f5546fdbb620028676d21e01638 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 18:38:17 +0200 Subject: [PATCH 117/268] Update /ask toasts to warnings for consistency --- public/scripts/slash-commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 9fe306279..6335e93b1 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -2481,7 +2481,7 @@ async function askCharacter(args, text) { // Not supported in group chats // TODO: Maybe support group chats? if (selected_group) { - toastr.error('Cannot run /ask command in a group chat!'); + toastr.warning('Cannot run /ask command in a group chat!'); return ''; } @@ -2501,7 +2501,7 @@ async function askCharacter(args, text) { // Find the character const chId = characters.findIndex((e) => e.name === name || e.avatar === name); if (!characters[chId] || chId === -1) { - toastr.error('Character not found.'); + toastr.warning('Character not found.'); return ''; } From 140aeb2bb7516e2890181a4065ae88b63c2966bc Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 20:22:44 +0200 Subject: [PATCH 118/268] Fix piping not using array for empty unnamed args - Lenny said if `splitUnnamedArgument` is enabled, the callback should always receive an array. - It is intended that if no value was provided, it'll get an array with an empty string. Because.. if no argument was provided for a non-split arg, it'll receive an empty string too. --- public/scripts/slash-commands/SlashCommandClosure.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/scripts/slash-commands/SlashCommandClosure.js b/public/scripts/slash-commands/SlashCommandClosure.js index 45c4d48ba..0ba1726b5 100644 --- a/public/scripts/slash-commands/SlashCommandClosure.js +++ b/public/scripts/slash-commands/SlashCommandClosure.js @@ -508,6 +508,14 @@ export class SlashCommandClosure { return v; }); } + + value ??= ''; + + // Make sure that if unnamed args are split, it should always return an array + if (executor.command.splitUnnamedArgument && !Array.isArray(value)) { + value = [value]; + } + return value; } From 0d38e634714ccb254799b390293066e54912bc37 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 20:28:52 +0200 Subject: [PATCH 119/268] Why join and then split again, eh? - Refactor /add actually using the array provided as the array for the internal `parseNumericSeries` inside `performOperation` --- public/scripts/variables.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index e9cd8c653..4e998efae 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -669,8 +669,8 @@ function deleteGlobalVariable(name) { } /** - * Parses a series of numeric values from a string. - * @param {string} value A space-separated list of numeric values or variable names + * Parses a series of numeric values from a string or a string array. + * @param {string|string[]} value A space-separated list of numeric values or variable names * @param {SlashCommandScope} scope Scope * @returns {number[]} An array of numeric values */ @@ -679,9 +679,8 @@ function parseNumericSeries(value, scope = null) { return [value]; } - const array = value - .split(' ') - .map(i => i.trim()) + const values = Array.isArray(value) ? value : value.split(' '); + const array = values.map(i => i.trim()) .filter(i => i !== '') .map(i => isNaN(Number(i)) ? Number(resolveVariable(i, scope)) : Number(i)) .filter(i => !isNaN(i)); @@ -1595,7 +1594,7 @@ export function registerVariableCommands() { })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'add', - callback: (args, /**@type {string[]}*/value) => addValuesCallback(args, value.join(' ')), + callback: (args, value) => addValuesCallback(args, value), returns: 'sum of the provided values', unnamedArgumentList: [ SlashCommandArgument.fromProps({ From 4855f254192de90d39e235c72d3e8511d5581e96 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 20:45:39 +0200 Subject: [PATCH 120/268] Update /mul, /max and /min definition - Update command definition for /mul, /max and /min to fit the actual code behind, split them too - Add numbersAndVariables enum provider, to centralize --- .../SlashCommandCommonEnumsProvider.js | 29 +++++++++++++++++ public/scripts/variables.js | 32 ++++--------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js index 5612f47b5..a6a589238 100644 --- a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js +++ b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js @@ -152,6 +152,35 @@ export const commonEnumProviders = { ].filter((item, idx, list)=>idx == list.findIndex(it=>it.value == item.value)); }, + /** + * Enum values for numbers and variable names + * + * Includes all variable names and the ability to specify any number + * + * @param {SlashCommandExecutor} executor - The executor of the slash command + * @param {SlashCommandScope} scope - The scope of the slash command + * @returns {SlashCommandEnumValue[]} The enum values + */ + numbersAndVariables: (executor, scope) => [ + ...commonEnumProviders.variables('all')(executor, scope), + new SlashCommandEnumValue( + 'any variable name', + null, + enumTypes.variable, + enumIcons.variable, + (input) => /^\w*$/.test(input), + (input) => input, + ), + new SlashCommandEnumValue( + 'any number', + null, + enumTypes.number, + enumIcons.number, + (input) => input == '' || !Number.isNaN(Number(input)), + (input) => input, + ), + ], + /** * All possible char entities, like characters and groups. Can be filtered down to just one type. * diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 4e998efae..bb53a487a 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -1602,28 +1602,7 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, acceptsMultiple: true, - enumProvider: (executor, scope) => { - const vars = commonEnumProviders.variables('all')(executor, scope); - vars.push( - new SlashCommandEnumValue( - 'any variable name', - null, - enumTypes.variable, - enumIcons.variable, - (input) => /^\w*$/.test(input), - (input) => input, - ), - new SlashCommandEnumValue( - 'any number', - null, - enumTypes.number, - enumIcons.number, - (input) => input == '' || !Number.isNaN(Number(input)), - (input) => input, - ), - ); - return vars; - }, + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1653,10 +1632,11 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, acceptsMultiple: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Performs a multiplication of the set of values and passes the result down the pipe. Can use variable names. @@ -1681,10 +1661,11 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, acceptsMultiple: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Returns the maximum value of the set of values and passes the result down the pipe. Can use variable names. @@ -1709,10 +1690,11 @@ export function registerVariableCommands() { typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, acceptsMultiple: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Returns the minimum value of the set of values and passes the result down the pipe. From 224249a0d21dd076ebbf73bc990ac8f66e7137f6 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 20:50:18 +0200 Subject: [PATCH 121/268] Fix /sub actually allowing more than two vals now - Fix by subtracting all from the first value - Update the definition too --- public/scripts/variables.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index bb53a487a..5e3449f02 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -730,7 +730,7 @@ function maxValuesCallback(args, value) { } function subValuesCallback(args, value) { - return performOperation(value, (array) => array[0] - array[1], false, args._scope); + return performOperation(value, (array) => array.reduce((a, b) => a - b, array.shift() ?? 0), false, args._scope); } function divValuesCallback(args, value) { @@ -1716,14 +1716,15 @@ export function registerVariableCommands() { returns: 'difference of the provided values', unnamedArgumentList: [ SlashCommandArgument.fromProps({ - description: 'values to find the difference', + description: 'values to subtract, starting form the first provided value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, acceptsMultiple: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Performs a subtraction of the set of values and passes the result down the pipe. From 9af4d62cdfbb7842bb4b665fc90652039b71100c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:51:24 +0300 Subject: [PATCH 122/268] Extend /sd command --- .../extensions/stable-diffusion/index.js | 317 ++++++++++++++---- .../extensions/stable-diffusion/settings.html | 15 +- src/endpoints/stable-diffusion.js | 69 ---- 3 files changed, 261 insertions(+), 140 deletions(-) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index ab0b1bcb5..18da313b5 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -20,7 +20,7 @@ import { } from '../../../script.js'; import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js'; import { selected_group } from '../../group-chats.js'; -import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce } from '../../utils.js'; +import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean } from '../../utils.js'; import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js'; import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js'; @@ -31,6 +31,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' import { debounce_timeout } from '../../constants.js'; import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js'; +import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; export { MODULE_NAME }; const MODULE_NAME = 'sd'; @@ -221,7 +222,6 @@ const defaultSettings = { // Refine mode refine_mode: false, - expand: false, interactive_mode: false, multimodal_captioning: false, snap: false, @@ -240,7 +240,7 @@ const defaultSettings = { drawthings_auth: '', hr_upscaler: 'Latent', - hr_scale: 2.0, + hr_scale: 1.0, hr_scale_min: 1.0, hr_scale_max: 4.0, hr_scale_step: 0.1, @@ -260,10 +260,6 @@ const defaultSettings = { clip_skip: 1, // NovelAI settings - novel_upscale_ratio_min: 1.0, - novel_upscale_ratio_max: 4.0, - novel_upscale_ratio_step: 0.1, - novel_upscale_ratio: 1.0, novel_anlas_guard: false, novel_sm: false, novel_sm_dyn: false, @@ -416,7 +412,6 @@ async function loadSettings() { $('#sd_hr_scale').val(extension_settings.sd.hr_scale).trigger('input'); $('#sd_denoising_strength').val(extension_settings.sd.denoising_strength).trigger('input'); $('#sd_hr_second_pass_steps').val(extension_settings.sd.hr_second_pass_steps).trigger('input'); - $('#sd_novel_upscale_ratio').val(extension_settings.sd.novel_upscale_ratio).trigger('input'); $('#sd_novel_anlas_guard').prop('checked', extension_settings.sd.novel_anlas_guard); $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm); $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); @@ -430,7 +425,6 @@ async function loadSettings() { $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces); $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr); $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode); - $('#sd_expand').prop('checked', extension_settings.sd.expand); $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning); $('#sd_auto_url').val(extension_settings.sd.auto_url); $('#sd_auto_auth').val(extension_settings.sd.auto_auth); @@ -644,37 +638,13 @@ async function onSaveStyleClick() { saveSettingsDebounced(); } -async function expandPrompt(prompt) { - try { - const response = await fetch('/api/sd/expand', { - method: 'POST', - headers: getRequestHeaders(), - body: JSON.stringify({ prompt: prompt }), - }); - - if (!response.ok) { - throw new Error('API returned an error.'); - } - - const data = await response.json(); - return data.prompt; - } catch { - return prompt; - } -} - /** - * Modifies prompt based on auto-expansion and user inputs. + * Modifies prompt based on user inputs. * @param {string} prompt Prompt to refine - * @param {boolean} allowExpand Whether to allow auto-expansion * @param {boolean} isNegative Whether the prompt is a negative one * @returns {Promise} Refined prompt */ -async function refinePrompt(prompt, allowExpand, isNegative = false) { - if (allowExpand && extension_settings.sd.expand) { - prompt = await expandPrompt(prompt); - } - +async function refinePrompt(prompt, isNegative) { if (extension_settings.sd.refine_mode) { const text = isNegative ? '

    Review and edit the negative prompt:

    ' : '

    Review and edit the prompt:

    '; const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' }); @@ -800,11 +770,6 @@ function combinePrefixes(str1, str2, macro = '') { return process(result); } -function onExpandInput() { - extension_settings.sd.expand = !!$(this).prop('checked'); - saveSettingsDebounced(); -} - function onRefineModeInput() { extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked'); saveSettingsDebounced(); @@ -969,12 +934,6 @@ async function onViewAnlasClick() { toastr.info(`Free image generation: ${unlimitedGeneration ? 'Yes' : 'No'}`, `Anlas: ${anlas}`); } -function onNovelUpscaleRatioInput() { - extension_settings.sd.novel_upscale_ratio = Number($('#sd_novel_upscale_ratio').val()); - $('#sd_novel_upscale_ratio_value').val(extension_settings.sd.novel_upscale_ratio.toFixed(1)); - saveSettingsDebounced(); -} - function onNovelAnlasGuardInput() { extension_settings.sd.novel_anlas_guard = !!$('#sd_novel_anlas_guard').prop('checked'); saveSettingsDebounced(); @@ -2272,6 +2231,23 @@ function getRawLastMessage() { return `((${processReply(lastMessage.mes)})), (${processReply(character.scenario)}:0.7), (${processReply(character.description)}:0.5)`; } +/** + * Ensure that the selected option exists in the dropdown. + * @param {string} setting Setting key + * @param {string} selector Dropdown selector + * @returns {void} + */ +function ensureSelectionExists(setting, selector) { + /** @type {HTMLSelectElement} */ + const selectElement = document.querySelector(selector); + if (!selectElement) { + return; + } + if (selectElement.selectedOptions.length && !Array.from(selectElement.options).some(option => option.value === extension_settings.sd[setting])) { + extension_settings.sd[setting] = selectElement.selectedOptions[0].value; + } +} + /** * Generates an image based on the given trigger word. * @param {string} initiator The initiator of the image generation @@ -2292,8 +2268,8 @@ async function generatePicture(initiator, args, trigger, message, callback) { return; } - extension_settings.sd.sampler = $('#sd_sampler').find(':selected').val(); - extension_settings.sd.model = $('#sd_model').find(':selected').val(); + ensureSelectionExists('sampler', '#sd_sampler'); + ensureSelectionExists('model', '#sd_model'); trigger = trigger.trim(); const generationType = getGenerationType(trigger); @@ -2441,7 +2417,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt, combineN } if (generationType !== generationMode.FREE) { - prompt = await refinePrompt(prompt, true); + prompt = await refinePrompt(prompt, false); } return prompt; @@ -3031,7 +3007,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) { width: width, height: height, negative_prompt: negativePrompt, - upscale_ratio: extension_settings.sd.novel_upscale_ratio, + upscale_ratio: extension_settings.sd.hr_scale, decrisper: extension_settings.sd.novel_decrisper, sm: sm, sm_dyn: sm_dyn, @@ -3613,8 +3589,8 @@ async function sdMessageButton(e) { try { setBusyIcon(true); if (hasSavedImage) { - const prompt = await refinePrompt(message.extra.title, false, false); - const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, false, true) : ''; + const prompt = await refinePrompt(message.extra.title, false); + const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, true) : ''; message.extra.title = prompt; const generationType = message?.extra?.generationType ?? generationMode.FREE; @@ -3756,8 +3732,8 @@ async function onImageSwiped({ message, element, direction }) { eventSource.once(CUSTOM_STOP_EVENT, stopListener); const callback = () => { }; const hasNegative = message.extra.negative; - const prompt = await refinePrompt(message.extra.title, false, false); - const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, false, true) : ''; + const prompt = await refinePrompt(message.extra.title, false); + const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, true) : ''; const characterName = context.groupId ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString() : context.characters[context.characterId]?.name; @@ -3788,12 +3764,83 @@ async function onImageSwiped({ message, element, direction }) { await context.saveChat(); } +/** + * Applies the command arguments to the extension settings. + * @typedef {import('../../slash-commands/SlashCommand.js').NamedArguments} NamedArguments + * @typedef {import('../../slash-commands/SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture + * @param {NamedArguments | NamedArgumentsCapture} args - Command arguments + * @returns {Record} - Current settings before applying the command arguments + */ +function applyCommandArguments(args) { + const overrideSettings = {}; + const currentSettings = {}; + const settingMap = { + 'edit': 'refine_mode', + 'extend': 'free_extend', + 'multimodal': 'multimodal_captioning', + 'seed': 'seed', + 'width': 'width', + 'height': 'height', + 'steps': 'steps', + 'cfg': 'scale', + 'skip': 'clip_skip', + 'model': 'model', + 'sampler': 'sampler', + 'scheduler': 'scheduler', + 'vae': 'vae', + 'upscaler': 'hr_upscaler', + 'scale': 'hr_scale', + 'hires': 'enable_hr', + 'denoise': 'denoising_strength', + '2ndpass': 'hr_second_pass_steps', + 'faces': 'restore_faces', + }; + + for (const [param, setting] of Object.entries(settingMap)) { + if (args[param] === undefined || defaultSettings[setting] === undefined) { + continue; + } + currentSettings[setting] = extension_settings.sd[setting]; + const value = String(args[param]); + const type = typeof defaultSettings[setting]; + switch (type) { + case 'boolean': + overrideSettings[setting] = isTrueBoolean(value) || !isFalseBoolean(value); + break; + case 'number': + overrideSettings[setting] = Number(value); + break; + default: + overrideSettings[setting] = value; + break; + } + } + + Object.assign(extension_settings.sd, overrideSettings); + return currentSettings; +} + jQuery(async () => { await addSDGenButtons(); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine', - callback: (args, trigger) => generatePicture(initiators.command, args, String(trigger)), + returns: 'URL of the generated image, or an empty string if the generation failed', + callback: async (args, trigger) => { + const currentSettings = applyCommandArguments(args); + + try { + return await generatePicture(initiators.command, args, String(trigger)); + } catch (error) { + console.error('Failed to generate image:', error); + return ''; + } finally { + if (Object.keys(currentSettings).length) { + Object.assign(extension_settings.sd, currentSettings); + saveSettingsDebounced(); + } + } + }, aliases: ['sd', 'img', 'image'], namedArgumentList: [ new SlashCommandNamedArgument( @@ -3803,6 +3850,164 @@ jQuery(async () => { name: 'negative', description: 'negative prompt prefix', typeList: [ARGUMENT_TYPE.STRING], + isRequired: false, + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'extend', + description: 'auto-extend free mode prompts with the LLM', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumProvider: commonEnumProviders.boolean('trueFalse'), + isRequired: false, + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'edit', + description: 'edit the prompt before generation', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumProvider: commonEnumProviders.boolean('trueFalse'), + isRequired: false, + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'multimodal', + description: 'use multimodal captioning (for portraits only)', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumProvider: commonEnumProviders.boolean('trueFalse'), + isRequired: false, + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'snap', + description: 'snap auto-adjusted dimensions to the nearest known resolution (portraits and backgrounds only)', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumProvider: commonEnumProviders.boolean('trueFalse'), + isRequired: false, + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'seed', + description: 'random seed', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'width', + description: 'image width', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'height', + description: 'image height', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'steps', + description: 'number of steps', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'cfg', + description: 'CFG scale', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'skip', + description: 'CLIP skip layers', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'model', + description: 'model override', + isRequired: false, + typeList: [ARGUMENT_TYPE.STRING], + acceptsMultiple: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_model > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + }), + SlashCommandNamedArgument.fromProps({ + name: 'sampler', + description: 'sampler override', + isRequired: false, + typeList: [ARGUMENT_TYPE.STRING], + acceptsMultiple: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_sampler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + }), + SlashCommandNamedArgument.fromProps({ + name: 'scheduler', + description: 'scheduler override', + isRequired: false, + typeList: [ARGUMENT_TYPE.STRING], + acceptsMultiple: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_scheduler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + }), + SlashCommandNamedArgument.fromProps({ + name: 'vae', + description: 'VAE name override', + isRequired: false, + typeList: [ARGUMENT_TYPE.STRING], + acceptsMultiple: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_vae > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + }), + SlashCommandNamedArgument.fromProps({ + name: 'upscaler', + description: 'upscaler override', + isRequired: false, + typeList: [ARGUMENT_TYPE.STRING], + acceptsMultiple: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_hr_upscaler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + }), + SlashCommandNamedArgument.fromProps({ + name: 'hires', + description: 'enable high-res fix', + isRequired: false, + typeList: [ARGUMENT_TYPE.BOOLEAN], + acceptsMultiple: false, + enumProvider: commonEnumProviders.boolean('trueFalse'), + }), + SlashCommandNamedArgument.fromProps({ + name: 'scale', + description: 'upscale amount', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'denoise', + description: 'denoising strength', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: '2ndpass', + description: 'second pass steps', + isRequired: false, + typeList: [ARGUMENT_TYPE.NUMBER], + acceptsMultiple: false, + }), + SlashCommandNamedArgument.fromProps({ + name: 'faces', + description: 'restore faces', + isRequired: false, + typeList: [ARGUMENT_TYPE.BOOLEAN], + acceptsMultiple: false, + enumProvider: commonEnumProviders.boolean('trueFalse'), }), ], unnamedArgumentList: [ @@ -3874,7 +4079,6 @@ jQuery(async () => { $('#sd_hr_scale').on('input', onHrScaleInput); $('#sd_denoising_strength').on('input', onDenoisingStrengthInput); $('#sd_hr_second_pass_steps').on('input', onHrSecondPassStepsInput); - $('#sd_novel_upscale_ratio').on('input', onNovelUpscaleRatioInput); $('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput); $('#sd_novel_view_anlas').on('click', onViewAnlasClick); $('#sd_novel_sm').on('input', onNovelSmInput); @@ -3887,7 +4091,6 @@ jQuery(async () => { $('#sd_comfy_open_workflow_editor').on('click', onComfyOpenWorkflowEditorClick); $('#sd_comfy_new_workflow').on('click', onComfyNewWorkflowClick); $('#sd_comfy_delete_workflow').on('click', onComfyDeleteWorkflowClick); - $('#sd_expand').on('input', onExpandInput); $('#sd_style').on('change', onStyleSelect); $('#sd_save_style').on('click', onSaveStyleClick); $('#sd_delete_style').on('click', onDeleteStyleClick); diff --git a/public/scripts/extensions/stable-diffusion/settings.html b/public/scripts/extensions/stable-diffusion/settings.html index f43ed7246..2efa74d83 100644 --- a/public/scripts/extensions/stable-diffusion/settings.html +++ b/public/scripts/extensions/stable-diffusion/settings.html @@ -27,11 +27,6 @@ Extend free mode prompts (interactive/commands) -
    -
    +
    Upscale by @@ -332,14 +327,6 @@
    -
    - - Upscale by - - - -
    -
    CLIP Skip diff --git a/src/endpoints/stable-diffusion.js b/src/endpoints/stable-diffusion.js index 9eff948f1..b6348b8b2 100644 --- a/src/endpoints/stable-diffusion.js +++ b/src/endpoints/stable-diffusion.js @@ -9,41 +9,6 @@ const { jsonParser } = require('../express-common'); const { readSecret, SECRET_KEYS } = require('./secrets.js'); const FormData = require('form-data'); -/** - * Sanitizes a string. - * @param {string} x String to sanitize - * @returns {string} Sanitized string - */ -function safeStr(x) { - x = String(x); - x = x.replace(/ +/g, ' '); - x = x.trim(); - x = x.replace(/^[\s,.]+|[\s,.]+$/g, ''); - return x; -} - -const splitStrings = [ - ', extremely', - ', intricate,', -]; - -const dangerousPatterns = '[]【】()()|::'; - -/** - * Removes patterns from a string. - * @param {string} x String to sanitize - * @param {string} pattern Pattern to remove - * @returns {string} Sanitized string - */ -function removePattern(x, pattern) { - for (let i = 0; i < pattern.length; i++) { - let p = pattern[i]; - let regex = new RegExp('\\' + p, 'g'); - x = x.replace(regex, ''); - } - return x; -} - /** * Gets the comfy workflows. * @param {import('../users.js').UserDirectoryList} directories @@ -391,40 +356,6 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => { } }); -/** - * SD prompt expansion using GPT-2 text generation model. - * Adapted from: https://github.com/lllyasviel/Fooocus/blob/main/modules/expansion.py - */ -router.post('/expand', jsonParser, async (request, response) => { - const originalPrompt = request.body.prompt; - - if (!originalPrompt) { - console.warn('No prompt provided for SD expansion.'); - return response.send({ prompt: '' }); - } - - console.log('Refine prompt input:', originalPrompt); - const splitString = splitStrings[Math.floor(Math.random() * splitStrings.length)]; - let prompt = safeStr(originalPrompt) + splitString; - - try { - const task = 'text-generation'; - const module = await import('../transformers.mjs'); - const pipe = await module.default.getPipeline(task); - - const result = await pipe(prompt, { num_beams: 1, max_new_tokens: 256, do_sample: true }); - - const newText = result[0].generated_text; - const newPrompt = safeStr(removePattern(newText, dangerousPatterns)); - console.log('Refine prompt output:', newPrompt); - - return response.send({ prompt: newPrompt }); - } catch { - console.warn('Failed to load transformers.js pipeline.'); - return response.send({ prompt: originalPrompt }); - } -}); - const comfy = express.Router(); comfy.post('/ping', jsonParser, async (request, response) => { From 8ff2ef086be4dd345344b98daf7111ae8ad21778 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 20:59:42 +0200 Subject: [PATCH 123/268] Update defs and enums for other math commands - Now even the commands like `/div count charnumber` work well with auto complete --- public/scripts/variables.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 5e3449f02..b06728359 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -1749,17 +1749,18 @@ export function registerVariableCommands() { description: 'dividend', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), SlashCommandArgument.fromProps({ description: 'divisor', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Performs a division of two values and passes the result down the pipe. @@ -1784,17 +1785,18 @@ export function registerVariableCommands() { description: 'dividend', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), SlashCommandArgument.fromProps({ description: 'divisor', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Performs a modulo operation of two values and passes the result down the pipe. @@ -1819,17 +1821,18 @@ export function registerVariableCommands() { description: 'base', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), SlashCommandArgument.fromProps({ description: 'exponent', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], + splitUnnamedArgument: true, helpString: `
    Performs a power operation of two values and passes the result down the pipe. @@ -1854,7 +1857,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1882,7 +1885,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1911,7 +1914,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1939,7 +1942,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1967,7 +1970,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], @@ -1995,7 +1998,7 @@ export function registerVariableCommands() { description: 'value', typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], isRequired: true, - enumProvider: commonEnumProviders.variables('all'), + enumProvider: commonEnumProviders.numbersAndVariables, forceEnum: false, }), ], From a4d02bd967132ae913524a4e73ff93c397b9302b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:00:03 +0300 Subject: [PATCH 124/268] Add /imagine-source command --- .../extensions/stable-diffusion/index.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index 18da313b5..24eb935dc 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -4028,6 +4028,38 @@ jQuery(async () => { `, })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'imagine-source', + aliases: ['sd-source', 'img-source'], + returns: 'a name of the current generation source', + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'source name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_source > [value]')).map(x => new SlashCommandEnumValue(x.getAttribute('value'), x.textContent)), + }), + ], + helpString: 'If an argument is provided, change the source of the image generation, e.g. /imagine-source comfy. Returns the current source.', + callback: async (_args, name) => { + if (!name) { + return extension_settings.sd.source; + } + const isKnownSource = Object.keys(sources).includes(String(name)); + if (!isKnownSource) { + throw new Error('The value provided is not a valid image generation source.'); + } + const option = document.querySelector(`#sd_source [value="${name}"]`); + if (!(option instanceof HTMLOptionElement)) { + throw new Error('Could not find the source option in the dropdown.'); + } + option.selected = true; + await onSourceChange(); + return extension_settings.sd.source; + }, + })) + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine-comfy-workflow', callback: changeComfyWorkflow, From 2dc7b5ded1e48224447ac4cab179dc3a01a59338 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 21:24:22 +0200 Subject: [PATCH 125/268] Allow using JSON arrays for math commands - applies to all that receive a list. /add, /sub, /min, /max etc - Parsing is the same as the other commands where we already allow "LIST" as an argument. --- public/scripts/variables.js | 60 +++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index b06728359..0e18f404a 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -679,10 +679,17 @@ function parseNumericSeries(value, scope = null) { return [value]; } - const values = Array.isArray(value) ? value : value.split(' '); - const array = values.map(i => i.trim()) + /** @type {(string|number)[]} */ + let values = Array.isArray(value) ? value : value.split(' '); + + // If a JSON array was provided as the only value, convert it to an array + if (values.length === 1 && typeof values[0] === 'string' && values[0].startsWith('[')) { + values = convertValueType(values[0], 'array'); + } + + const array = values.map(i => typeof i === 'string' ? i.trim() : i) .filter(i => i !== '') - .map(i => isNaN(Number(i)) ? Number(resolveVariable(i, scope)) : Number(i)) + .map(i => isNaN(Number(i)) ? Number(resolveVariable(String(i), scope)) : Number(i)) .filter(i => !isNaN(i)); return array; @@ -1599,7 +1606,7 @@ export function registerVariableCommands() { unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'values to sum', - typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], isRequired: true, acceptsMultiple: true, enumProvider: commonEnumProviders.numbersAndVariables, @@ -1610,7 +1617,9 @@ export function registerVariableCommands() { helpString: `
    Performs an addition of the set of values and passes the result down the pipe. - Can use variable names. +
    +
    + Can use variable names, or a JSON array consisting of numbers and variables (with quotes).
    Example: @@ -1618,6 +1627,9 @@ export function registerVariableCommands() {
  • /add 10 i 30 j
  • +
  • +
    /add ["count", 15, 2, "i"]
    +
  • `, @@ -1629,7 +1641,7 @@ export function registerVariableCommands() { unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'values to multiply', - typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], isRequired: true, acceptsMultiple: true, enumProvider: commonEnumProviders.numbersAndVariables, @@ -1639,7 +1651,10 @@ export function registerVariableCommands() { splitUnnamedArgument: true, helpString: `
    - Performs a multiplication of the set of values and passes the result down the pipe. Can use variable names. + Performs a multiplication of the set of values and passes the result down the pipe. +
    +
    + Can use variable names, or a JSON array consisting of numbers and variables (with quotes).
    Examples: @@ -1647,6 +1662,9 @@ export function registerVariableCommands() {
  • /mul 10 i 30 j
  • +
  • +
    /mul ["count", 15, 2, "i"]
    +
  • `, @@ -1658,7 +1676,7 @@ export function registerVariableCommands() { unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'values to find the max', - typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], isRequired: true, acceptsMultiple: true, enumProvider: commonEnumProviders.numbersAndVariables, @@ -1668,7 +1686,10 @@ export function registerVariableCommands() { splitUnnamedArgument: true, helpString: `
    - Returns the maximum value of the set of values and passes the result down the pipe. Can use variable names. + Returns the maximum value of the set of values and passes the result down the pipe. +
    +
    + Can use variable names, or a JSON array consisting of numbers and variables (with quotes).
    Examples: @@ -1676,6 +1697,9 @@ export function registerVariableCommands() {
  • /max 10 i 30 j
  • +
  • +
    /max ["count", 15, 2, "i"]
    +
  • `, @@ -1687,7 +1711,7 @@ export function registerVariableCommands() { unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'values to find the min', - typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], isRequired: true, acceptsMultiple: true, enumProvider: commonEnumProviders.numbersAndVariables, @@ -1698,7 +1722,9 @@ export function registerVariableCommands() { helpString: `
    Returns the minimum value of the set of values and passes the result down the pipe. - Can use variable names. +
    +
    + Can use variable names, or a JSON array consisting of numbers and variables (with quotes).
    Example: @@ -1706,6 +1732,9 @@ export function registerVariableCommands() {
  • /min 10 i 30 j
  • +
  • +
    /min ["count", 15, 2, "i"]
    +
  • `, @@ -1717,7 +1746,7 @@ export function registerVariableCommands() { unnamedArgumentList: [ SlashCommandArgument.fromProps({ description: 'values to subtract, starting form the first provided value', - typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], isRequired: true, acceptsMultiple: true, enumProvider: commonEnumProviders.numbersAndVariables, @@ -1728,7 +1757,9 @@ export function registerVariableCommands() { helpString: `
    Performs a subtraction of the set of values and passes the result down the pipe. - Can use variable names. +
    +
    + Can use variable names, or a JSON array consisting of numbers and variables (with quotes).
    Example: @@ -1736,6 +1767,9 @@ export function registerVariableCommands() {
  • /sub i 5
  • +
  • +
    /sub ["count", 4, "i"]
    +
  • `, From 3fd846fb5b46d876898dde344c26dbcd6da7f222 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:52:01 +0300 Subject: [PATCH 126/268] Allow returning literal Infinity string from math operations --- public/scripts/variables.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 0e18f404a..7c9830a36 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -709,7 +709,7 @@ function performOperation(value, operation, singleOperand = false, scope = null) const result = singleOperand ? operation(array[0]) : operation(array); - if (isNaN(result) || !isFinite(result)) { + if (isNaN(result)) { return 0; } From 0df5a86ae81a89d8681d729af29f5d40a6e5dbc7 Mon Sep 17 00:00:00 2001 From: notfiz Date: Tue, 1 Oct 2024 00:53:14 +0330 Subject: [PATCH 127/268] fix google-ai reverse proxy ignoring user provided path --- src/endpoints/backends/chat-completions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index a551f8fbc..ca86db051 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -323,7 +323,7 @@ async function sendMakerSuiteRequest(request, response) { ? (stream ? 'streamGenerateContent' : 'generateContent') : (isText ? 'generateText' : 'generateMessage'); - const generateResponse = await fetch(`${apiUrl.origin}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, { + const generateResponse = await fetch(`${apiUrl}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, { body: JSON.stringify(body), method: 'POST', headers: { From d8379edee7d8fb0a99e742aa929dc02602490860 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 23:32:24 +0200 Subject: [PATCH 128/268] Update return types as optional via named arg - Update the modified slash commands for chat sending to use the named arg - Add `slashCommandReturnHelper` for shared funcitonality on return type usage --- public/scripts/slash-commands.js | 37 ++++++++-- .../SlashCommandCommonEnumsProvider.js | 1 + .../SlashCommandReturnHelper.js | 74 +++++++++++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 public/scripts/slash-commands/SlashCommandReturnHelper.js diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 6335e93b1..599d6b850 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -71,6 +71,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js'; import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; +import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; export { executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, }; @@ -176,7 +177,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sendas', callback: sendMessageAs, - returns: 'The text of the sent message', + returns: 'Optionally the text of the sent message, if specified in the "return" argument', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'name', @@ -195,6 +196,14 @@ export function initDefaultSlashCommands() { typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'none', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -223,7 +232,7 @@ export function initDefaultSlashCommands() { name: 'sys', callback: sendNarratorMessage, aliases: ['nar'], - returns: 'The text of the sent message', + returns: 'Optionally the text of the sent message, if specified in the "return" argument', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -239,6 +248,14 @@ export function initDefaultSlashCommands() { typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'none', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -278,7 +295,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'comment', callback: sendCommentMessage, - returns: 'The text of the sent message', + returns: 'Optionally the text of the sent message, if specified in the "return" argument', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -294,6 +311,14 @@ export function initDefaultSlashCommands() { typeList: [ARGUMENT_TYPE.NUMBER], enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'none', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -3210,7 +3235,7 @@ export async function sendMessageAs(args, text) { await saveChatConditional(); } - return message.mes; + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); } export async function sendNarratorMessage(args, text) { @@ -3264,7 +3289,7 @@ export async function sendNarratorMessage(args, text) { await saveChatConditional(); } - return message.mes; + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); } export async function promptQuietForLoudResponse(who, text) { @@ -3353,7 +3378,7 @@ async function sendCommentMessage(args, text) { await saveChatConditional(); } - return message.mes; + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); } /** diff --git a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js index 5612f47b5..bd5a7e8a3 100644 --- a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js +++ b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js @@ -36,6 +36,7 @@ export const enumIcons = { message: '💬', voice: '🎤', server: '🖥️', + popup: '🗔', true: '✔️', false: '❌', diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js new file mode 100644 index 000000000..90ec0d783 --- /dev/null +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -0,0 +1,74 @@ +import { sendSystemMessage, system_message_types } from '../../script.js'; +import { callGenericPopup, POPUP_TYPE } from '../popup.js'; +import { escapeHtml } from '../utils.js'; +import { enumIcons } from './SlashCommandCommonEnumsProvider.js'; +import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js'; + +/** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */ + +export const slashCommandReturnHelper = { + /** + * Gets/creates the enum list of types of return relevant for a slash command + * + * @param {object} [options={}] Options + * @param {boolean} [options.allowPipe=true] Allow option to pipe the return value + * @param {boolean} [options.allowObject=false] Allow option to return the value as an object + * @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message + * @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup + * @returns {SlashCommandEnumValue[]} The enum list + */ + enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false } = {}) => [ + allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'), + allowObject && new SlashCommandEnumValue('object', 'Return as an object to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), + allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message), + allowChat && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), + new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), + new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup), + new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'), + new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'), + new SlashCommandEnumValue('console', 'Log the return value to the console', enumTypes.enum, '>'), + new SlashCommandEnumValue('none', 'No return value'), + ].filter(x => !!x), + + /** + * Handles the return value based on the specified type + * + * @param {SlashCommandReturnType} type The type of return + * @param {object|number|string} value The value to return + * @param {object} [options={}] Options + * @param {(o: object) => string} [options.objectToStringFunc=null] Function to convert the object to a string, if object was provided and 'object' was not the chosen return type + * @returns {Promise<*>} The processed return value + */ + async doReturn(type, value, { objectToStringFunc = o => o?.toString() } = {}) { + const stringValue = typeof value !== 'string' ? objectToStringFunc(value) : value; + + switch (type) { + case 'popup-html': + case 'popup-text': + case 'chat-text': + case 'chat-html': + case 'toast-text': + case 'toast-html': { + const shouldHtml = type.endsWith('html'); + const makeHtml = (str) => (new showdown.Converter()).makeHtml(str); + + if (type.startsWith('popup')) await callGenericPopup(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), POPUP_TYPE.TEXT); + if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue)); + if (type.startsWith('toast')) toastr.info(stringValue, null, { escapeHtml: !shouldHtml }); // Toastr handles HTML conversion internally already + + return ''; + } + case 'pipe': + return stringValue ?? ''; + case 'object': + return JSON.stringify(value); + case 'console': + console.info(value); + return ''; + case 'none': + return ''; + default: + throw new Error(`Unknown return type: ${type}`); + } + }, +}; From e3c0c5442c1646970802cb1243bb9e39279d28be Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 30 Sep 2024 23:37:21 +0200 Subject: [PATCH 129/268] Update /ask with return types, defaulting 'pipe' --- public/scripts/slash-commands.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 599d6b850..792e54bf1 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -456,7 +456,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'ask', callback: askCharacter, - returns: 'the generated text', + returns: 'Optionally the text of the sent message, if specified in the "return" argument', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'name', @@ -465,6 +465,14 @@ export function initDefaultSlashCommands() { isRequired: true, enumProvider: commonEnumProviders.characters('character'), }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'pipe', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -2594,7 +2602,9 @@ async function askCharacter(args, text) { } } - return askResult; + const message = askResult ? chat[chat.length - 1] : null; + + return await slashCommandReturnHelper.doReturn(args.return ?? 'pipe', message, { objectToStringFunc: x => x.mes }); } async function hideMessageCallback(_, arg) { From 7ac7398568c0c1c6fcf8e708f5a8eaf9feeb5c04 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 00:37:33 +0300 Subject: [PATCH 130/268] Remove debug log. Fix comment --- public/scripts/openai.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index a8fbd667f..20c141316 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -1776,8 +1776,7 @@ async function sendOpenAIRequest(type, messages, signal) { generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt; generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings. generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message); - // Don't add a prefill on quiet gens (summarization) - console.log(isContinue && oai_settings.continue_prefill); + // Don't add a prefill on quiet gens (summarization) and when using continue prefill. if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) { generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill); } From 62fd450c592653fd256eaf631372cd05c72745ae Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 00:06:18 +0200 Subject: [PATCH 131/268] Refactor /listinjects, deprecate its "format" arg --- public/scripts/slash-commands.js | 74 +++++++++++-------- .../SlashCommandReturnHelper.js | 16 ++-- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 792e54bf1..17352b233 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1525,11 +1525,20 @@ export function initDefaultSlashCommands() { name: 'listinjects', callback: listInjectsCallback, helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the format argument to change the output format.', - returns: 'JSON object of script injections', + returns: 'Optionalls the JSON object of script injections', namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'object', + enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), + forceEnum: true, + }), + // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: 'output format', + description: 'DEPRECATED - use "return" instead - output format', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, forceEnum: true, @@ -1798,37 +1807,44 @@ function injectCallback(args, value) { } async function listInjectsCallback(args) { - const type = String(args?.format).toLowerCase().trim(); - if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { - type !== 'none' && toastr.info('No script injections for the current chat'); - return JSON.stringify({}); + /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ + let returnType = args.return; + + // Old legacy return type handling + if (args.format) { + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); + const type = String(args?.format).toLowerCase().trim(); + if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { + type !== 'none' && toastr.info('No script injections for the current chat'); + } + switch (type) { + case 'none': + returnType = 'none'; + break; + case 'chat': + returnType = 'chat-html'; + break; + case 'popup': + default: + returnType = 'popup-html'; + break; + } } - const injects = Object.entries(chat_metadata.script_injects) - .map(([id, inject]) => { - const position = Object.entries(extension_prompt_types); - const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; - return `* **${id}**: ${inject.value} (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; - }) - .join('\n'); + // Now the actual new return type handling - const converter = new showdown.Converter(); - const messageText = `### Script injections:\n${injects}`; - const htmlMessage = DOMPurify.sanitize(converter.makeHtml(messageText)); + const buildTextValue = (injects) => { + const injectsStr = Object.entries(injects) + .map(([id, inject]) => { + const position = Object.entries(extension_prompt_types); + const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; + return `* **${id}**: ${inject.value} (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; + }) + .join('\n'); + return `### Script injections:\n${injectsStr}`; + }; - switch (type) { - case 'none': - break; - case 'chat': - sendSystemMessage(system_message_types.GENERIC, htmlMessage); - break; - case 'popup': - default: - await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); - break; - } - - return JSON.stringify(chat_metadata.script_injects); + return await slashCommandReturnHelper.doReturn(returnType ?? 'object', chat_metadata.script_injects, { objectToStringFunc: buildTextValue }); } /** diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js index 90ec0d783..cdcc914cd 100644 --- a/public/scripts/slash-commands/SlashCommandReturnHelper.js +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -7,6 +7,9 @@ import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js'; /** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */ export const slashCommandReturnHelper = { + // Without this, VSCode formatter fucks up JS docs. Don't ask me why. + _: false, + /** * Gets/creates the enum list of types of return relevant for a slash command * @@ -15,18 +18,19 @@ export const slashCommandReturnHelper = { * @param {boolean} [options.allowObject=false] Allow option to return the value as an object * @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message * @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup + * @param {boolean}[options.allowTextVersion=true] Used in combination with chat/popup/toast, some of them do not make sense for text versions, e.g.if you are building a HTML string anyway * @returns {SlashCommandEnumValue[]} The enum list */ - enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false } = {}) => [ + enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false, allowTextVersion = true } = {}) => [ allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'), allowObject && new SlashCommandEnumValue('object', 'Return as an object to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message), - allowChat && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), - new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), - new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup), + allowChat && allowTextVersion && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), + allowPopup && new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), + allowPopup && allowTextVersion && new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup), new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'), - new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'), - new SlashCommandEnumValue('console', 'Log the return value to the console', enumTypes.enum, '>'), + allowTextVersion && new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'), + new SlashCommandEnumValue('console', 'Log the return value (object, if it can be one) to the console', enumTypes.enum, '>'), new SlashCommandEnumValue('none', 'No return value'), ].filter(x => !!x), From 697b3b2034c20ea15e48002929b696f85bfb3a64 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 00:23:00 +0200 Subject: [PATCH 132/268] Refactor /listvar, deprecate its "format" arg - Update /listvar - Fix toasts not doing correct HMTL here --- public/scripts/slash-commands.js | 9 +-- .../SlashCommandReturnHelper.js | 2 +- public/scripts/variables.js | 77 ++++++++++++------- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 17352b233..d74eedbb9 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1524,21 +1524,21 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'listinjects', callback: listInjectsCallback, - helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the format argument to change the output format.', + helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the return argument to change the return type.', returns: 'Optionalls the JSON object of script injections', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'return', description: 'The way how you want the return value to be provided', typeList: [ARGUMENT_TYPE.STRING], - defaultValue: 'object', + defaultValue: 'popup-html', enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), forceEnum: true, }), // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: 'DEPRECATED - use "return" instead - output format', + description: '!!! DEPRECATED - use "return" instead !!! output format)', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, forceEnum: true, @@ -1832,7 +1832,6 @@ async function listInjectsCallback(args) { } // Now the actual new return type handling - const buildTextValue = (injects) => { const injectsStr = Object.entries(injects) .map(([id, inject]) => { @@ -1844,7 +1843,7 @@ async function listInjectsCallback(args) { return `### Script injections:\n${injectsStr}`; }; - return await slashCommandReturnHelper.doReturn(returnType ?? 'object', chat_metadata.script_injects, { objectToStringFunc: buildTextValue }); + return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects, { objectToStringFunc: buildTextValue }); } /** diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js index cdcc914cd..941e1f302 100644 --- a/public/scripts/slash-commands/SlashCommandReturnHelper.js +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -58,7 +58,7 @@ export const slashCommandReturnHelper = { if (type.startsWith('popup')) await callGenericPopup(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), POPUP_TYPE.TEXT); if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue)); - if (type.startsWith('toast')) toastr.info(stringValue, null, { escapeHtml: !shouldHtml }); // Toastr handles HTML conversion internally already + if (type.startsWith('toast')) toastr.info(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), null, { escapeHtml: !shouldHtml }); return ''; } diff --git a/public/scripts/variables.js b/public/scripts/variables.js index e9cd8c653..55e9fe9ab 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -11,6 +11,7 @@ import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureR import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; @@ -305,7 +306,31 @@ export function replaceVariableMacros(input) { } async function listVariablesCallback(args) { - const type = String(args?.format || '').toLowerCase().trim() || 'popup'; + /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ + let returnType = args.return; + + // Old legacy return type handling + if (args.format) { + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); + const type = String(args?.format).toLowerCase().trim(); + if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { + type !== 'none' && toastr.info('No script injections for the current chat'); + } + switch (type) { + case 'none': + returnType = 'none'; + break; + case 'chat': + returnType = 'chat-html'; + break; + case 'popup': + default: + returnType = 'popup-html'; + break; + } + } + + // Now the actual new return type handling const scope = String(args?.scope || '').toLowerCase().trim() || 'all'; if (!chat_metadata.variables) { chat_metadata.variables = {}; @@ -317,35 +342,24 @@ async function listVariablesCallback(args) { const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : []; const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : []; + const buildTextValue = (_) => { + const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; + const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; + const chatName = getCurrentChatId(); + + const message = [ + includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', + includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', + ].filter(x => x).join('\n\n'); + return message; + }; + const jsonVariables = [ ...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })), ...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })), ]; - const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; - const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; - const chatName = getCurrentChatId(); - - const converter = new showdown.Converter(); - const message = [ - includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', - includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', - ].filter(x => x).join('\n\n'); - const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message)); - - switch (type) { - case 'none': - break; - case 'chat': - sendSystemMessage(system_message_types.GENERIC, htmlMessage); - break; - case 'popup': - default: - await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); - break; - } - - return JSON.stringify(jsonVariables); + return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', jsonVariables, { objectToStringFunc: buildTextValue }); } /** @@ -910,7 +924,7 @@ export function registerVariableCommands() { name: 'listvar', callback: listVariablesCallback, aliases: ['listchatvar'], - helpString: 'List registered chat variables. Displays variables in a popup by default. Use the format argument to change the output format.', + helpString: 'List registered chat variables. Displays variables in a popup by default. Use the return argument to change the return type.', returns: 'JSON list of local variables', namedArgumentList: [ SlashCommandNamedArgument.fromProps({ @@ -926,9 +940,18 @@ export function registerVariableCommands() { new SlashCommandEnumValue('global', 'Global variables', enumTypes.enum, enumIcons.globalVariable), ], }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'popup-html', + enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), + forceEnum: true, + }), + // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: 'output format', + description: '!!! DEPRECATED - use "return" instead !!! output format)', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, forceEnum: true, From 7a1b43eb8943b48747bd3efa139e81186346f964 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 00:37:21 +0200 Subject: [PATCH 133/268] Refactor /classify-expressions, deprecating... - Update /classify-expressions, deprecating the old "format" - Fix some oversights --- .../scripts/extensions/expressions/index.js | 40 +++++++++++++++---- public/scripts/slash-commands.js | 2 +- .../SlashCommandReturnHelper.js | 2 +- public/scripts/variables.js | 2 +- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js index 71be1e422..a37d011c5 100644 --- a/public/scripts/extensions/expressions/index.js +++ b/public/scripts/extensions/expressions/index.js @@ -12,6 +12,8 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' import { isFunctionCallingSupported } from '../../openai.js'; import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; +import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; +import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js'; export { MODULE_NAME }; const MODULE_NAME = 'expressions'; @@ -2128,18 +2130,42 @@ function migrateSettings() { name: 'classify-expressions', aliases: ['expressions'], callback: async (args) => { - const list = await getExpressionsList(); - switch (String(args.format).toLowerCase()) { - case 'json': - return JSON.stringify(list); - default: - return list.join(', '); + /** @type {import('../../slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ + // @ts-ignore + let returnType = args.return; + + // Old legacy return type handling + if (args.format) { + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); + const type = String(args?.format).toLowerCase().trim(); + switch (type) { + case 'json': + returnType = 'object'; + break; + default: + returnType = 'pipe'; + break; + } } + + // Now the actual new return type handling + const list = await getExpressionsList(); + + return await slashCommandReturnHelper.doReturn(returnType ?? 'pipe', list, { objectToStringFunc: list => list.join(', ') }); }, namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'pipe', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), + // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: 'The format to return the list in: comma-separated plain text or JSON array. Default is plain text.', + description: '!!! DEPRECATED - use "return" instead !!! The format to return the list in: comma-separated plain text or JSON array. Default is plain text.', typeList: [ARGUMENT_TYPE.STRING], enumList: [ new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '), diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index d74eedbb9..85744fdd8 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1538,7 +1538,7 @@ export function initDefaultSlashCommands() { // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: '!!! DEPRECATED - use "return" instead !!! output format)', + description: '!!! DEPRECATED - use "return" instead !!! output format', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, forceEnum: true, diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js index 941e1f302..9d34846bb 100644 --- a/public/scripts/slash-commands/SlashCommandReturnHelper.js +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -23,7 +23,7 @@ export const slashCommandReturnHelper = { */ enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false, allowTextVersion = true } = {}) => [ allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'), - allowObject && new SlashCommandEnumValue('object', 'Return as an object to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), + allowObject && new SlashCommandEnumValue('object', 'Return as an object (or array) to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message), allowChat && allowTextVersion && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), allowPopup && new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 55e9fe9ab..67844e375 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -951,7 +951,7 @@ export function registerVariableCommands() { // TODO remove some day SlashCommandNamedArgument.fromProps({ name: 'format', - description: '!!! DEPRECATED - use "return" instead !!! output format)', + description: '!!! DEPRECATED - use "return" instead !!! output format', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, forceEnum: true, From f317b1b764eb5013fd3d88defd91f5165890a58a Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 00:41:14 +0200 Subject: [PATCH 134/268] Oversight, missed /send in refactoring... --- public/scripts/slash-commands.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 85744fdd8..d1ab9523e 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -511,7 +511,7 @@ export function initDefaultSlashCommands() { SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'send', callback: sendUserMessageCallback, - returns: 'The text of the sent message', + returns: 'Optionally the text of the sent message, if specified in the "return" argument', namedArgumentList: [ new SlashCommandNamedArgument( 'compact', @@ -534,6 +534,14 @@ export function initDefaultSlashCommands() { defaultValue: '{{user}}', enumProvider: commonEnumProviders.personas, }), + SlashCommandNamedArgument.fromProps({ + name: 'return', + description: 'The way how you want the return value to be provided', + typeList: [ARGUMENT_TYPE.STRING], + defaultValue: 'none', + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), + forceEnum: true, + }), ], unnamedArgumentList: [ new SlashCommandArgument( @@ -2923,7 +2931,7 @@ async function sendUserMessageCallback(args, text) { message = await sendMessageAsUser(text, bias, insertAt, compact); } - return message.mes; + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); } async function deleteMessagesByNameCallback(_, name) { From 398ae6ba2e0c23fb12741ee2bfea52454e6a82cd Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 00:53:19 +0200 Subject: [PATCH 135/268] Update doReturn() API with objectToHtmlFunc - Allow future commands to provide a different "object to HTML" converter func than "object to text", if need be --- .../slash-commands/SlashCommandReturnHelper.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js index 9d34846bb..be1a2da5a 100644 --- a/public/scripts/slash-commands/SlashCommandReturnHelper.js +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -41,10 +41,13 @@ export const slashCommandReturnHelper = { * @param {object|number|string} value The value to return * @param {object} [options={}] Options * @param {(o: object) => string} [options.objectToStringFunc=null] Function to convert the object to a string, if object was provided and 'object' was not the chosen return type + * @param {(o: object) => string} [options.objectToHtmlFunc=null] Analog to 'objectToStringFunc', which will be used here if not provided - but can do a different string layout if HTML is requested * @returns {Promise<*>} The processed return value */ - async doReturn(type, value, { objectToStringFunc = o => o?.toString() } = {}) { - const stringValue = typeof value !== 'string' ? objectToStringFunc(value) : value; + async doReturn(type, value, { objectToStringFunc = o => o?.toString(), objectToHtmlFunc = null } = {}) { + const shouldHtml = type.endsWith('html'); + const actualConverterFunc = shouldHtml && objectToHtmlFunc ? objectToHtmlFunc : objectToStringFunc; + const stringValue = typeof value !== 'string' ? actualConverterFunc(value) : value; switch (type) { case 'popup-html': @@ -53,12 +56,11 @@ export const slashCommandReturnHelper = { case 'chat-html': case 'toast-text': case 'toast-html': { - const shouldHtml = type.endsWith('html'); - const makeHtml = (str) => (new showdown.Converter()).makeHtml(str); + const htmlOrNotHtml = shouldHtml ? (new showdown.Converter()).makeHtml(stringValue) : escapeHtml(stringValue); - if (type.startsWith('popup')) await callGenericPopup(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), POPUP_TYPE.TEXT); - if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue)); - if (type.startsWith('toast')) toastr.info(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), null, { escapeHtml: !shouldHtml }); + if (type.startsWith('popup')) await callGenericPopup(htmlOrNotHtml, POPUP_TYPE.TEXT); + if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, htmlOrNotHtml); + if (type.startsWith('toast')) toastr.info(htmlOrNotHtml, null, { escapeHtml: !shouldHtml }); return ''; } From a63f99b8ce3c3f20370f265ad4359088c5876df0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:11:24 +0300 Subject: [PATCH 136/268] Add /imagine-style command --- .../extensions/stable-diffusion/index.js | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index 24eb935dc..dd9c193b8 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -4058,7 +4058,35 @@ jQuery(async () => { await onSourceChange(); return extension_settings.sd.source; }, - })) + })); + + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'imagine-style', + aliases: ['sd-style', 'img-style'], + returns: 'a name of the current style', + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'style name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: false, + forceEnum: true, + enumProvider: () => Array.from(document.querySelectorAll('#sd_style > [value]')).map(x => new SlashCommandEnumValue(x.getAttribute('value'), x.textContent)), + }), + ], + helpString: 'If an argument is provided, change the style of the image generation, e.g. /imagine-style MyStyle. Returns the current style.', + callback: async (_args, name) => { + if (!name) { + return extension_settings.sd.style; + } + const option = document.querySelector(`#sd_style [value="${name}"]`); + if (!(option instanceof HTMLOptionElement)) { + throw new Error('Could not find the style option in the dropdown.'); + } + option.selected = true; + onStyleSelect(); + return extension_settings.sd.style; + }, + })); SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine-comfy-workflow', From e2dd7bfb4b582aeef33a55f7766740d439afd5ab Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:15:27 +0300 Subject: [PATCH 137/268] Remove leftovers from prompt expansion --- default/config.yaml | 1 - src/transformers.mjs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/default/config.yaml b/default/config.yaml index 585988686..a7e7a747e 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -118,7 +118,6 @@ extras: classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx captioningModel: Xenova/vit-gpt2-image-captioning embeddingModel: Cohee/jina-embeddings-v2-base-en - promptExpansionModel: Cohee/fooocus_expansion-onnx speechToTextModel: Xenova/whisper-small textToSpeechModel: Xenova/speecht5_tts # -- OPENAI CONFIGURATION -- diff --git a/src/transformers.mjs b/src/transformers.mjs index fe947123a..09714e3bb 100644 --- a/src/transformers.mjs +++ b/src/transformers.mjs @@ -31,12 +31,6 @@ const tasks = { configField: 'extras.embeddingModel', quantized: true, }, - 'text-generation': { - defaultModel: 'Cohee/fooocus_expansion-onnx', - pipeline: null, - configField: 'extras.promptExpansionModel', - quantized: false, - }, 'automatic-speech-recognition': { defaultModel: 'Xenova/whisper-small', pipeline: null, From 56265540dbeafc9afa0b6d8f5f26efa002d1b02f Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 01:23:34 +0200 Subject: [PATCH 138/268] sanitize HTML on html returns - I had it in there for some time, I even tested it... likely gone during some commits --- public/scripts/slash-commands/SlashCommandReturnHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands/SlashCommandReturnHelper.js b/public/scripts/slash-commands/SlashCommandReturnHelper.js index be1a2da5a..3601c30cd 100644 --- a/public/scripts/slash-commands/SlashCommandReturnHelper.js +++ b/public/scripts/slash-commands/SlashCommandReturnHelper.js @@ -56,7 +56,7 @@ export const slashCommandReturnHelper = { case 'chat-html': case 'toast-text': case 'toast-html': { - const htmlOrNotHtml = shouldHtml ? (new showdown.Converter()).makeHtml(stringValue) : escapeHtml(stringValue); + const htmlOrNotHtml = shouldHtml ? DOMPurify.sanitize((new showdown.Converter()).makeHtml(stringValue)) : escapeHtml(stringValue); if (type.startsWith('popup')) await callGenericPopup(htmlOrNotHtml, POPUP_TYPE.TEXT); if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, htmlOrNotHtml); From f6726db9fb87efe785c57e2d8a32b6a97faee117 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 1 Oct 2024 01:30:20 +0200 Subject: [PATCH 139/268] Fix empty script injections on /listinjects --- public/scripts/slash-commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 9347bf016..98738e5c3 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -1929,10 +1929,10 @@ async function listInjectsCallback(args) { return `* **${id}**: ${inject.value} (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; }) .join('\n'); - return `### Script injections:\n${injectsStr}`; + return `### Script injections:\n${injectsStr || 'No script injections for the current chat'}`; }; - return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects, { objectToStringFunc: buildTextValue }); + return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects ?? {}, { objectToStringFunc: buildTextValue }); } /** From 6b61abc8edc033c3e5e88e69ec398e70df8305ea Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:30:24 +0300 Subject: [PATCH 140/268] Extract logic for enum providers --- .../extensions/stable-diffusion/index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index dd9c193b8..7d87757e0 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -3823,6 +3823,8 @@ function applyCommandArguments(args) { jQuery(async () => { await addSDGenButtons(); + const getSelectEnumProvider = (id, text) => () => Array.from(document.querySelectorAll(`#${id} > [value]`)).map(x => new SlashCommandEnumValue(x.getAttribute('value'), text ? x.textContent : null)); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine', returns: 'URL of the generated image, or an empty string if the generation failed', @@ -3934,7 +3936,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], acceptsMultiple: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_model > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + enumProvider: getSelectEnumProvider('sd_model', true), }), SlashCommandNamedArgument.fromProps({ name: 'sampler', @@ -3943,7 +3945,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], acceptsMultiple: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_sampler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + enumProvider: getSelectEnumProvider('sd_sampler', false), }), SlashCommandNamedArgument.fromProps({ name: 'scheduler', @@ -3952,7 +3954,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], acceptsMultiple: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_scheduler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + enumProvider: getSelectEnumProvider('sd_scheduler', false), }), SlashCommandNamedArgument.fromProps({ name: 'vae', @@ -3961,7 +3963,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], acceptsMultiple: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_vae > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + enumProvider: getSelectEnumProvider('sd_vae', false), }), SlashCommandNamedArgument.fromProps({ name: 'upscaler', @@ -3970,7 +3972,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], acceptsMultiple: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_hr_upscaler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)), + enumProvider: getSelectEnumProvider('sd_hr_upscaler', false), }), SlashCommandNamedArgument.fromProps({ name: 'hires', @@ -4038,7 +4040,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], isRequired: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_source > [value]')).map(x => new SlashCommandEnumValue(x.getAttribute('value'), x.textContent)), + enumProvider: getSelectEnumProvider('sd_source', true), }), ], helpString: 'If an argument is provided, change the source of the image generation, e.g. /imagine-source comfy. Returns the current source.', @@ -4070,7 +4072,7 @@ jQuery(async () => { typeList: [ARGUMENT_TYPE.STRING], isRequired: false, forceEnum: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_style > [value]')).map(x => new SlashCommandEnumValue(x.getAttribute('value'), x.textContent)), + enumProvider: getSelectEnumProvider('sd_style', false), }), ], helpString: 'If an argument is provided, change the style of the image generation, e.g. /imagine-style MyStyle. Returns the current style.', @@ -4097,7 +4099,7 @@ jQuery(async () => { description: 'workflow name', typeList: [ARGUMENT_TYPE.STRING], isRequired: true, - enumProvider: () => Array.from(document.querySelectorAll('#sd_comfy_workflow > [value]')).map(x => x.getAttribute('value')).map(workflow => new SlashCommandEnumValue(workflow)), + enumProvider: getSelectEnumProvider('sd_comfy_workflow', false), }), ], helpString: '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g.
    /imagine-comfy-workflow MyWorkflow
    ', From 6c110e91f7769f0c12990cf12f61875b3daf8989 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:32:32 +0300 Subject: [PATCH 141/268] Exit scripts when try to update non-git installs --- UpdateAndStart.bat | 11 +++++++++-- UpdateForkAndStart.bat | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/UpdateAndStart.bat b/UpdateAndStart.bat index 55cee5ce9..8bf70f368 100644 --- a/UpdateAndStart.bat +++ b/UpdateAndStart.bat @@ -2,9 +2,15 @@ pushd %~dp0 git --version > nul 2>&1 if %errorlevel% neq 0 ( - echo Git is not installed on this system. Skipping update. - echo If you installed with a zip file, you will need to download the new zip and install it manually. + echo Git is not installed on this system. + echo Install it from https://git-scm.com/downloads + goto end ) else ( + if not exist .git ( + echo Not running from a Git repository. Reinstall using an officially supported method to get updates. + echo See: https://docs.sillytavern.app/installation/windows/ + goto end + ) call git pull --rebase --autostash if %errorlevel% neq 0 ( REM incase there is still something wrong @@ -14,5 +20,6 @@ if %errorlevel% neq 0 ( set NODE_ENV=production call npm install --no-audit --no-fund --loglevel=error --no-progress --omit=dev node server.js %* +:end pause popd diff --git a/UpdateForkAndStart.bat b/UpdateForkAndStart.bat index 8bfae8609..8088962bb 100644 --- a/UpdateForkAndStart.bat +++ b/UpdateForkAndStart.bat @@ -5,8 +5,14 @@ pushd %~dp0 echo Checking Git installation git --version > nul 2>&1 if %errorlevel% neq 0 ( - echo Git is not installed on this system. Skipping update. - echo If you installed with a zip file, you will need to download the new zip and install it manually. + echo Git is not installed on this system. + echo Install it from https://git-scm.com/downloads + goto end +) + +if not exist .git ( + echo Not running from a Git repository. Reinstall using an officially supported method to get updates. + echo See: https://docs.sillytavern.app/installation/windows/ goto end ) From 9e3307576ef835da5ae399c80f7f865ecd964059 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:43:20 +0300 Subject: [PATCH 142/268] Crash when update failed --- UpdateAndStart.bat | 4 +++- UpdateForkAndStart.bat | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/UpdateAndStart.bat b/UpdateAndStart.bat index 8bf70f368..fbb3ac9bb 100644 --- a/UpdateAndStart.bat +++ b/UpdateAndStart.bat @@ -14,7 +14,9 @@ if %errorlevel% neq 0 ( call git pull --rebase --autostash if %errorlevel% neq 0 ( REM incase there is still something wrong - echo There were errors while updating. Please download the latest version manually. + echo There were errors while updating. + echo See the update FAQ at https://docs.sillytavern.app/usage/update/#common-update-problems + goto end ) ) set NODE_ENV=production diff --git a/UpdateForkAndStart.bat b/UpdateForkAndStart.bat index 8088962bb..d788b9470 100644 --- a/UpdateForkAndStart.bat +++ b/UpdateForkAndStart.bat @@ -95,7 +95,8 @@ git pull --rebase --autostash origin %TARGET_BRANCH% :install if %errorlevel% neq 0 ( - echo There were errors while updating. Please check manually. + echo There were errors while updating. + echo See the update FAQ at https://docs.sillytavern.app/usage/update/#common-update-problems goto end ) From b4529e75c6adafd9f66fd138799675ff411d88f8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:16:55 +0300 Subject: [PATCH 143/268] Don't wrap empty sysprompts in instruct mode --- public/scripts/instruct-mode.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/scripts/instruct-mode.js b/public/scripts/instruct-mode.js index 033f5c3e8..ba641935f 100644 --- a/public/scripts/instruct-mode.js +++ b/public/scripts/instruct-mode.js @@ -384,6 +384,10 @@ export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvata * @returns {string} Formatted instruct mode system prompt. */ export function formatInstructModeSystemPrompt(systemPrompt) { + if (!systemPrompt) { + return ''; + } + const separator = power_user.instruct.wrap ? '\n' : ''; if (power_user.instruct.system_sequence_prefix) { From 5489dd61a758e991196eedc8e1fb34293f328afc Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:14:19 +0300 Subject: [PATCH 144/268] Pass /continue error to slash command executor --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 22531eb15..0a30f24da 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3027,7 +3027,7 @@ async function continueChatCallback(args, prompt) { resolve(); } catch (error) { console.error('Error running /continue command:', error); - reject(); + reject(error); } }); From 42d24dbc477a08256918ebd73c0b895185c61338 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:24:27 +0300 Subject: [PATCH 145/268] Fix free mode extension on empty chats when {{charPrefix}} is used --- public/scripts/extensions/stable-diffusion/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index 7d87757e0..20ffccac1 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -2445,7 +2445,7 @@ function generateFreeModePrompt(trigger, combineNegatives) { return message.original_avatar.replace(/\.[^/.]+$/, ''); } } - throw new Error('No usable messages found.'); + return ''; }; const key = getLastCharacterKey(); From ab966db1e5ae9a7a6248314fd55529789f342b0e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:28:34 +0300 Subject: [PATCH 146/268] Refactor ensureSelectionExists for more readability --- public/scripts/extensions/stable-diffusion/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index 20ffccac1..e96575d56 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -2243,7 +2243,9 @@ function ensureSelectionExists(setting, selector) { if (!selectElement) { return; } - if (selectElement.selectedOptions.length && !Array.from(selectElement.options).some(option => option.value === extension_settings.sd[setting])) { + const options = Array.from(selectElement.options); + const value = extension_settings.sd[setting]; + if (selectElement.selectedOptions.length && !options.some(option => option.value === value)) { extension_settings.sd[setting] = selectElement.selectedOptions[0].value; } } From c76fc7d23c95ea663d22b594d23a965d33dd7a8c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:02:14 +0300 Subject: [PATCH 147/268] /model: Better support for write-in controls --- public/scripts/slash-commands.js | 47 +++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 0a30f24da..1e0413b7b 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -55,7 +55,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; -import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; +import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; import { registerVariableCommands, resolveVariable } from './variables.js'; import { background_settings } from './backgrounds.js'; import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; @@ -3542,11 +3542,12 @@ function setBackgroundCallback(_, bg) { * Retrieves the available model options based on the currently selected main API and its subtype * @param {boolean} quiet - Whether to suppress toasts * - * @returns {{control: HTMLSelectElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported + * @returns {{control: HTMLSelectElement|HTMLInputElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported */ function getModelOptions(quiet) { const nullResult = { control: null, options: null }; const modelSelectMap = [ + { id: 'custom_model_textgenerationwebui', api: 'textgenerationwebui', type: textgen_types.OOBA }, { id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI }, { id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER }, { id: 'model_infermaticai_select', api: 'textgenerationwebui', type: textgen_types.INFERMATICAI }, @@ -3563,7 +3564,7 @@ function getModelOptions(quiet) { { id: 'model_ai21_select', api: 'openai', type: chat_completion_sources.AI21 }, { id: 'model_google_select', api: 'openai', type: chat_completion_sources.MAKERSUITE }, { id: 'model_mistralai_select', api: 'openai', type: chat_completion_sources.MISTRALAI }, - { id: 'model_custom_select', api: 'openai', type: chat_completion_sources.CUSTOM }, + { id: 'custom_model_id', api: 'openai', type: chat_completion_sources.CUSTOM }, { id: 'model_cohere_select', api: 'openai', type: chat_completion_sources.COHERE }, { id: 'model_perplexity_select', api: 'openai', type: chat_completion_sources.PERPLEXITY }, { id: 'model_groq_select', api: 'openai', type: chat_completion_sources.GROQ }, @@ -3594,12 +3595,31 @@ function getModelOptions(quiet) { const modelSelectControl = document.getElementById(modelSelectItem); - if (!(modelSelectControl instanceof HTMLSelectElement)) { + if (!(modelSelectControl instanceof HTMLSelectElement) && !(modelSelectControl instanceof HTMLInputElement)) { !quiet && toastr.error(`Model select control not found: ${main_api}[${apiSubType}]`); return nullResult; } - const options = Array.from(modelSelectControl.options).filter(x => x.value); + /** + * Get options from a HTMLSelectElement or HTMLInputElement with a list. + * @param {HTMLSelectElement | HTMLInputElement} control Control containing the options + * @returns {HTMLOptionElement[]} Array of options + */ + const getOptions = (control) => { + if (control instanceof HTMLSelectElement) { + return Array.from(control.options); + } + + const valueOption = new Option(control.value, control.value); + + if (control instanceof HTMLInputElement && control.list instanceof HTMLDataListElement) { + return [valueOption, ...Array.from(control.list.options)]; + } + + return [valueOption]; + }; + + const options = getOptions(modelSelectControl).filter(x => x.value).filter(onlyUnique); return { control: modelSelectControl, options }; } @@ -3618,11 +3638,6 @@ function modelCallback(args, model) { return ''; } - if (!options.length) { - !quiet && toastr.warning('No model options found. Check your API settings.'); - return ''; - } - model = String(model || '').trim(); if (!model) { @@ -3631,6 +3646,18 @@ function modelCallback(args, model) { console.log('Set model to ' + model); + if (modelSelectControl instanceof HTMLInputElement) { + modelSelectControl.value = model; + $(modelSelectControl).trigger('input'); + !quiet && toastr.success(`Model set to "${model}"`); + return model; + } + + if (!options.length) { + !quiet && toastr.warning('No model options found. Check your API settings.'); + return ''; + } + let newSelectedOption = null; const fuse = new Fuse(options, { keys: ['text', 'value'] }); From 7f9023d3c21cb026914efd5489add59b71e21297 Mon Sep 17 00:00:00 2001 From: Yokayo Date: Tue, 1 Oct 2024 21:25:20 +0700 Subject: [PATCH 148/268] Work on tl --- public/locales/ru-ru.json | 147 +++++++++++++++++- public/script.js | 123 +++++++-------- public/scripts/PromptManager.js | 13 +- public/scripts/authors-note.js | 19 +-- public/scripts/chats.js | 25 +-- public/scripts/extensions/regex/editor.html | 2 +- public/scripts/group-chats.js | 20 +-- public/scripts/openai.js | 53 +++---- public/scripts/templates/assistantNote.html | 2 +- public/scripts/templates/deleteConfirm.html | 5 + .../scripts/templates/duplicateConfirm.html | 4 +- public/scripts/templates/hotkeys.html | 14 +- public/scripts/templates/quotaError.html | 4 + 13 files changed, 286 insertions(+), 145 deletions(-) create mode 100644 public/scripts/templates/deleteConfirm.html create mode 100644 public/scripts/templates/quotaError.html diff --git a/public/locales/ru-ru.json b/public/locales/ru-ru.json index 7472d199d..8cdb2325b 100644 --- a/public/locales/ru-ru.json +++ b/public/locales/ru-ru.json @@ -451,8 +451,8 @@ "Creator's Notes": "Заметки создателя", "A-Z": "A-Z", "Z-A": "Z-A", - "Newest": "Новейшие", - "Oldest": "Старейшие", + "Newest": "Сначала новые", + "Oldest": "Сначала старые", "Favorites": "Избранные", "Recent": "Последние", "Most chats": "Больше всего чатов", @@ -564,7 +564,7 @@ "Advanced Definition": "Расширенное описание", "Character Lore": "Лор персонажа", "Export and Download": "Экспортировать и скачать", - "Duplicate Character": "Дублировать персонажа", + "Duplicate Character": "Клонировать персонажа", "Create Character": "Создать персонажа", "Delete Character": "Удалить персонажа", "View all tags": "Показать все тэги", @@ -1690,7 +1690,7 @@ "Delete Message": "Удалить сообщение", "Delete Swipe": "Удалить свайп", "Could not get a reply from API. Check your connection settings / API key and try again.": "Не удалось получить ответ от API. Проверьте настройки соединения и API-ключ и повторите попытку.", - "Connecting To Proxy": "Подключение к прокси", + "Connecting To Proxy": "Подключиться к прокси", "Are you sure you want to connect to the following proxy URL?": "Вы точно хотите соединиться с прокси по этому адресу?", "API connection successful!": "Соединение с API установлено!", "Proxy Saved": "Прокси сохранена", @@ -1747,5 +1747,142 @@ "Markdown Hotkeys": "Горячие клавиши для разметки", "markdown_hotkeys_desc": "Включить горячие клавиши для вставки символов разметки в некоторых полях ввода. См. '/help hotkeys'.", "Save and Update": "Сохранить и обновить", - "Profile name:": "Название профиля:" + "Profile name:": "Название профиля:", + "API returned an error": "API вернуло ошибку", + "Failed to save preset": "Не удалось сохранить пресет", + "Preset name should be unique.": "Название пресета должно быть уникальным.", + "Invalid file": "Невалидный файл", + "No preset selected": "Пресет не выбран", + "Invalid logit bias preset file.": "Файл пресета невалиден.", + "Preset was not deleted from server": "Пресет не удалён с сервера", + "Preset deleted": "Пресет удалён", + "Delete the preset?": "Удалить пресет?", + "Preset updated": "Пресет обновлён", + "Entered reverse proxy address is not a valid URL": "Введённый адрес прокси невалиден", + "An error occurred while counting tokens: Token budget exceeded.": "Ошибка при подсчёте токенов: Превышен бюджет токенов", + "An error occurred while counting tokens: Invalid character name": "Ошибка при подсчёте токенов: Невалидное имя персонажа", + "Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.": "Недостаточно токенов для всех выбранных промптов. Повысьте лимит токенов или отключите часть промптов.", + "The name of at least one character contained whitespaces or special characters. Please check your user and character name.": "В имени одного из персонажей содержится пробел или иной спецсимвол. Проверьте имена пользователя и персонажа.", + "An unknown error occurred while counting tokens. Further information may be available in console.": "Неизвестная ошибка при подсчёте токенов. Проверьте консоль, возможно, подробная информация есть там.", + "quota_error_1": "При обработке вашего запроса возникла ошибка.", + "quota_error_2": "Убедитесь, что на вашем", + "quota_error_3": "аккаунте OpenAI", + "quota_error_4": "имеется достаточно кредитов.", + "quota_error_5": "Если кредитов достаточно, то повторите попытку позднее.", + "Proxy preset '${0}' not found": "Пресет '${0}' не найден", + "Window.ai returned an error": "Window.ai вернул ошибку", + "Get it here:": "Загрузите здесь:", + "Extension is not installed": "Расширение не установлено", + "Update or remove your reverse proxy settings.": "Измените или удалите ваши настройки прокси.", + "An error occurred while importing prompts. More info available in console.": "В процессе импорта произошла ошибка. Подробную информацию см. в консоли.", + "Could not import prompts. Export failed validation.": "Не удалось импортировать промпты. Не пройдена валидация при экспорте.", + "Prompt import complete.": "Импорт завершён.", + "Are you sure you want to delete this prompt?": "Вы точно хотите удалить этот промпт?", + "Existing prompts with the same ID will be overridden. Do you want to proceed?": "Имеющиеся промпты с совпадающими идентификаторами будут перезаписаны. Продолжить?", + "This will reset the prompt order for this character. You will not lose any prompts.": "Будет сброшен порядок промптов для этого персонажа. Сами промпты вы не потеряете.", + "assistant_note_1": "Примечание:", + "assistant_note_2": "это временный чат, он будет удалён, как только вы из него выйдете.", + "help_hotkeys_20": "Горячие клавиши для разметки", + "help_hotkeys_21": "Работают в окне ввода чата, а также в полях, отмеченных этим значком:", + "help_hotkeys_22": "**полужирный**", + "help_hotkeys_23": "*курсив*", + "help_hotkeys_24": "__подчёркивание__", + "help_hotkeys_25": "`inline-код`", + "help_hotkeys_26": "~~зачёркнутый~~", + "ext_regex_only_format_visual_desc": "Содержимое файла с историей чата останется нетронутым, изменения будут лишь визуальными (в UI).", + "Could not convert file": "Не удалось сконвертировать файл", + "Could not upload file": "Не удалось загрузить файл", + "Could not download file": "Не удалось скачать файл", + "File is too big. Maximum size is ${0}.": "Слишком большой файл. Максимальный размер: ${0}.", + "Binary files are not supported. Select a text file or image.": "Бинарные файлы не поддерживаются. Выберите текстовый файл или изображение.", + "No character or group selected": "Не выбрано ни одного персонажа или группы", + "Could not delete file": "Не удалось удалить файл", + "No attachments selected.": "Вложение не выбрано.", + "Data Bank": "Банк данных", + "No files were scraped.": "Не соскраплено ни одного файла.", + "Scraped ${0} files from ${1} to ${2}.": "Соскраплено ${0} файлов из ${1} в ${2}.", + "Check browser console for details.": "Подробности см. в консоли браузера.", + "Scraping failed": "Ошибка скрапинга", + "External media has been blocked": "Внешние медиа отключены", + "Use the 'Ext. Media' button to allow it. Click on this message to dismiss.": "Разрешить можно с помощью кнопки 'Внешн. медиа'. Нажмите на это сообщение, чтобы его скрыть.", + "Couldn't get CSRF token. Please refresh the page.": "Не удалось получить CSRF токен. Попробуйте перезагрузить страницу.", + "Error": "Ошибка", + "API Error": "Ошибка API", + "Please wait until the chat is saved before switching characters.": "Пожалуйста, дождитесь сохранения чата, прежде чем переключать персонажа.", + "Your chat is still saving...": "Чат всё ещё сохраняется...", + "Character ${0} not found in the list": "Персонаж ${0} не найден в списке", + "Streaming is enabled, but the version of Kobold used does not support token streaming.": "Включён стриминг текста, но ваша версия Kobold не поддерживает стриминг токенов.", + "Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.": "Для устаревшего API стриминг недоступен. Обновите oobaboga и используйте новый API, чтобы включить стриминг.", + "Verify that the server is running and accessible.": "Убедитесь, что сервер запущен и доступен по сети.", + "ST Server cannot be reached": "Не удалось соединиться с ST Server", + "You must first select a character to duplicate!": "Вы не выбрали персонажа, которого хотите клонировать!", + "Character Duplicated": "Персонаж склонирован", + "No character name provided.": "Вы не ввели имя персонажа.", + "Rename Character": "Переименование", + "No character selected.": "Вы не выбрали персонажа.", + "New name:": "Новое имя:", + "Same character name provided, so name did not change.": "Введено то же самое имя, ничего не изменилось.", + "Character renamed and past chats updated!": "Персонаж переименован, а чаты обновлены!", + "Character renamed!": "Персонаж переименован!", + "Something went wrong. The page will be reloaded.": "Что-то пошло не так. Страница будет перезагружена.", + "Past chat could not be updated: ${0}": "Не удалось обновить чат ${0}", + "Trying to save group chat with regular saveChat function. Aborting to prevent corruption.": "Произошла попытка сохранения группового чата функцией saveChat. Откатываем изменения, чтобы предотвратить потерю данных.", + "Check the server connection and reload the page to prevent data loss.": "Проверьте связь с сервером и перезагрузите страницу, чтобы избежать потери данных.", + "Chat could not be saved": "Не удалось сохранить чат", + "Settings could not be loaded after multiple attempts. Please try again later.": "Не удалось загрузить настройки за несколько попыток. Попробуйте позднее.", + "Settings could not be saved": "Не удалось сохранить настройки", + "Could not load chat data. Try reloading the page.": "Не удалось загрузить чат. Попробуйте обновить страницу.", + "Invalid process (no 'type')": "Невалидный процесс (нет параметра 'type')", + "Character Deleted: ${0}": "Персонаж удалён: ${0}", + "Character Created: ${0}": "Персонаж создан: ${0}", + "Group Created": "Группа создана", + "Group Deleted": "Группа удалена", + "Character Imported: ${0}": "Персонаж импортирован: ${0}", + "Invalid swipe ID: ${0}": "Некорректный идентификатор свайпа: ${0}", + "No messages to delete swipes from.": "Сообщение, из которого требуется удалить свайп, не найдено.", + "Can't delete the last swipe.": "Невозможно удалить единственный свайп.", + "GUI Settings preset is not supported for Horde. Please select another preset.": "Для Horde не поддерживаются пресеты настроек GUI. Пожалуйста, выберите другой пресет.", + "Embedded lorebook will be removed from this character.": "Встроенный лорбук будет удалён из персонажа.", + "Name is required": "Введите имя", + "Cannot create characters while generating. Stop the request and try again.": "Во время генерации ответа создать персонажа невозможно. Остановите запрос и повторите попытку.", + "Creation aborted": "Процесс создания прерван", + "Failed to create character": "Не удалось создать персонажа", + "Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.": "Что-то пошло не так в процессе сохранения персонажа, либо вы загрузили файл некорректного формата. Проверьте, что изображение точно не в формате webp.", + "Context template '${0}' not found": "Шаблон контекста '${0}' не найден", + "Instruct template '${0}' not found": "Шаблон Instruct-режима '${0}' не найден", + "Error: ${0} is not a valid API": "Ошибка: ${0} не является валидным API", + "API set to ${0}, trying to connect..": "Установлено API ${0}, пробуем подключиться...", + "Unsupported file type: ": "Неподдерживаемый тип файла: ", + "Cannot import characters while generating. Stop the request and try again.": "Во время генерации ответа импорт персонажа невозможен. Остановите запрос и повторите попытку.", + "Import aborted": "Процесс импорта прерван", + "The file is likely invalid or corrupted.": "Вероятно, файл невалиден или повреждён.", + "Could not import character": "Не удалось импортировать персонажа", + "Cannot run /impersonate command while the reply is being generated.": "Во время генерации ответа выполнить /impersonate невозможно.", + "Name must be provided as an argument to rename this chat.": "Для переименования чата необходимо предоставить новое имя в качестве аргумента.", + "No chat selected that can be renamed.": "Чат, который требуется переименовать, не найден.", + "Successfully renamed chat to: ${0}": "Чат успешно переименован в: ${0}", + "Character ${0} not found. Skipping deletion.": "Персонаж ${0} не найден. Удаление пропускается.", + "Failed to delete character": "При удалении персонажа произошло ошибка", + "duplicate_confirm_1": "Вы точно хотите клонировать этого персонажа?", + "duplicate_confirm_2": "Если вы хотите просто создать новый чат, воспользуйтесь кнопкой \"Начать новый чат\" в меню слева внизу.", + "delete_confirm_1": "ОТМЕНИТЬ БУДЕТ НЕВОЗМОЖНО!", + "delete_confirm_2": "Также удалить файлы чатов", + "Delete the character?": "Удалить персонажа?", + "Not a valid number": "Некорректное число", + "Author's Note depth updated": "Глубина заметок автора обновлена", + "Author's Note frequency updated": "Частота заметок автора обновлена", + "Not a valid position": "Некорректная позиция", + "Author's Note position updated": "Позиция заметок автора обновлена", + "Something went wrong. Could not save character's author's note.": "Что-то пошло не так. Не удалось сохранить заметки автора для этого персонажа.", + "Select a character before trying to use Author's Note": "Сначала необходимо выбрать персонажа", + "Author's Note text updated": "Текст заметок автора обновлён", + "Group Validation": "Валидация группы", + "Warning: Listed member ${0} does not exist as a character. It will be removed from the group.": "Предупреждение: персонаж ${0} не существует в виде карточки. Он будет удалён из группы.", + "Group Chat could not be saved": "Не удалось сохранить групповой чат", + "Deleted group member swiped. To get a reply, add them back to the group.": "Вы пытаетесь свайпнуть удалённого члена группы. Чтобы получить ответ, добавьте этого персонажа обратно в группу.", + "Currently no group selected.": "В данный момент не выбрано ни одной группы.", + "Not so fast! Wait for the characters to stop typing before deleting the group.": "Чуть помедленнее! Перед удалением группы дождитесь, пока персонаж закончит печатать.", + "Delete the group?": "Удалить группу?", + "This will also delete all your chats with that group. If you want to delete a single conversation, select a \"View past chats\" option in the lower left menu.": "Вместе с ней будут удалены и все её чаты. Если требуется удалить только один чат, воспользуйтесь кнопкой \"Все чаты\" в меню в левом нижнем углу.", + "Can't peek a character while group reply is being generated": "Невозможно открыть карточку персонажа во время генерации ответа" } diff --git a/public/script.js b/public/script.js index 8d085b6d2..139c64a37 100644 --- a/public/script.js +++ b/public/script.js @@ -383,8 +383,8 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { if (localStorage.getItem(warningShownKey) === null) { const warningToast = toastr.warning( - 'Use the "Ext. Media" button to allow it. Click on this message to dismiss.', - 'External media has been blocked', + t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`, + t`External media has been blocked`, { timeOut: 0, preventDuplicates: true, @@ -924,7 +924,7 @@ async function firstLoadInit() { token = tokenData.token; } catch { hideLoader(); - toastr.error('Couldn\'t get CSRF token. Please refresh the page.', 'Error', { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true }); + toastr.error(t`Couldn't get CSRF token. Please refresh the page.`, t`Error`, { timeOut: 0, extendedTimeOut: 0, preventDuplicates: true }); throw new Error('Initialization failed'); } @@ -1140,7 +1140,7 @@ async function getStatusKobold() { // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. if (online_status === 'no_connection' && data.response) { - toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true }); + toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true }); } } catch (err) { console.error('Error getting status', err); @@ -1226,7 +1226,7 @@ async function getStatusTextgen() { // We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress. if (online_status === 'no_connection' && data.response) { - toastr.error(data.response, 'API Error', { timeOut: 5000, preventDuplicates: true }); + toastr.error(data.response, t`API Error`, { timeOut: 5000, preventDuplicates: true }); } } catch (err) { if (err instanceof AbortReason) { @@ -1278,7 +1278,7 @@ export async function selectCharacterById(id) { } if (isChatSaving) { - toastr.info('Please wait until the chat is saved before switching characters.', 'Your chat is still saving...'); + toastr.info(t`Please wait until the chat is saved before switching characters.`, t`Your chat is still saving...`); return; } @@ -1632,7 +1632,7 @@ export async function getOneCharacter(avatarUrl) { if (indexOf !== -1) { characters[indexOf] = getData; } else { - toastr.error(`Character ${avatarUrl} not found in the list`, 'Error', { timeOut: 5000, preventDuplicates: true }); + toastr.error(t`Character ${avatarUrl} not found in the list`, t`Error`, { timeOut: 5000, preventDuplicates: true }); } } } @@ -3374,7 +3374,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro await eventSource.emit(event_types.GENERATION_AFTER_COMMANDS, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun); if (main_api == 'kobold' && kai_settings.streaming_kobold && !kai_flags.can_use_streaming) { - toastr.error('Streaming is enabled, but the version of Kobold used does not support token streaming.', undefined, { timeOut: 10000, preventDuplicates: true }); + toastr.error(t`Streaming is enabled, but the version of Kobold used does not support token streaming.`, undefined, { timeOut: 10000, preventDuplicates: true }); unblockGeneration(type); return Promise.resolve(); } @@ -3383,7 +3383,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro textgen_settings.streaming && textgen_settings.legacy_api && textgen_settings.type === OOBA) { - toastr.error('Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.', undefined, { timeOut: 10000, preventDuplicates: true }); + toastr.error(t`Streaming is not supported for the Legacy API. Update Ooba and use new API to enable streaming.`, undefined, { timeOut: 10000, preventDuplicates: true }); unblockGeneration(type); return Promise.resolve(); } @@ -3399,7 +3399,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro if (!pingResult) { unblockGeneration(type); - toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached'); + toastr.error(t`Verify that the server is running and accessible.`, t`ST Server cannot be reached`); throw new Error('Server unreachable'); } @@ -4427,7 +4427,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro generatedPromptCache = ''; if (data?.response) { - toastr.error(data.response, 'API Error', { preventDuplicates: true }); + toastr.error(data.response, t`API Error`, { preventDuplicates: true }); } throw new Error(data?.response); } @@ -4518,7 +4518,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro function onError(exception) { if (typeof exception?.error?.message === 'string') { - toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 }); + toastr.error(exception.error.message, t`Error`, { timeOut: 10000, extendedTimeOut: 20000 }); } generatedPromptCache = ''; @@ -4920,7 +4920,7 @@ function addChatsSeparator(mesSendString) { async function duplicateCharacter() { if (!this_chid) { - toastr.warning('You must first select a character to duplicate!'); + toastr.warning(t`You must first select a character to duplicate!`); return ''; } @@ -4939,7 +4939,7 @@ async function duplicateCharacter() { body: JSON.stringify(body), }); if (response.ok) { - toastr.success('Character Duplicated'); + toastr.success(t`Character Duplicated`); const data = await response.json(); await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); await getCharacters(); @@ -5786,23 +5786,23 @@ export function setSendButtonState(value) { export async function renameCharacter(name = null, { silent = false, renameChats = null } = {}) { if (!name && silent) { - toastr.warning('No character name provided.', 'Rename Character'); + toastr.warning(t`No character name provided.`, t`Rename Character`); return false; } if (this_chid === undefined) { - toastr.warning('No character selected.', 'Rename Character'); + toastr.warning(t`No character selected.`, t`Rename Character`); return false; } const oldAvatar = characters[this_chid].avatar; - const newValue = name || await callGenericPopup('

    New name:

    ', POPUP_TYPE.INPUT, characters[this_chid].name); + const newValue = name || await callGenericPopup('

    ' + t`New name:` + '

    ', POPUP_TYPE.INPUT, characters[this_chid].name); if (!newValue) { - toastr.warning('No character name provided.', 'Rename Character'); + toastr.warning(t`No character name provided.`, t`Rename Character`); return false; } if (newValue === characters[this_chid].name) { - toastr.info('Same character name provided, so name did not change.', 'Rename Character'); + toastr.info(t`Same character name provided, so name did not change.`, t`Rename Character`); return false; } @@ -5849,9 +5849,9 @@ export async function renameCharacter(name = null, { silent = false, renameChats if (renamePastChatsConfirm) { await renamePastChats(newAvatar, newValue); await reloadCurrentChat(); - toastr.success('Character renamed and past chats updated!', 'Rename Character'); + toastr.success(t`Character renamed and past chats updated!`, t`Rename Character`); } else { - toastr.success('Character renamed!', 'Rename Character'); + toastr.success(t`Character renamed!`, t`Rename Character`); } } else { @@ -5864,8 +5864,8 @@ export async function renameCharacter(name = null, { silent = false, renameChats } catch (error) { // Reloading to prevent data corruption - if (!silent) await callPopup('Something went wrong. The page will be reloaded.', 'text'); - else toastr.error('Something went wrong. The page will be reloaded.', 'Rename Character'); + if (!silent) await callPopup(t`Something went wrong. The page will be reloaded.`, 'text'); + else toastr.error(t`Something went wrong. The page will be reloaded.`, t`Rename Character`); console.log('Renaming character error:', error); location.reload(); @@ -5922,7 +5922,7 @@ async function renamePastChats(newAvatar, newValue) { } } } catch (error) { - toastr.error(`Past chat could not be updated: ${file_name}`); + toastr.error(t`Past chat could not be updated: ${file_name}`); console.error(error); } } @@ -5971,7 +5971,7 @@ export async function saveChat(chatName, withMetadata, mesId) { characters[this_chid]['date_last_chat'] = Date.now(); chat.forEach(function (item, i) { if (item['is_group']) { - toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'); + toastr.error(t`Trying to save group chat with regular saveChat function. Aborting to prevent corruption.`); throw new Error('Group chat saved from saveChat'); } /* @@ -6016,7 +6016,7 @@ export async function saveChat(chatName, withMetadata, mesId) { contentType: 'application/json', success: function (data) { }, error: function (jqXHR, exception) { - toastr.error('Check the server connection and reload the page to prevent data loss.', 'Chat could not be saved'); + toastr.error(t`Check the server connection and reload the page to prevent data loss.`, t`Chat could not be saved`); console.log(exception); console.log(jqXHR); }, @@ -6413,7 +6413,7 @@ export async function getSettings() { if (!response.ok) { reloadLoop(); - toastr.error('Settings could not be loaded after multiple attempts. Please try again later.'); + toastr.error(t`Settings could not be loaded after multiple attempts. Please try again later.`); throw new Error('Error getting settings'); } @@ -6640,7 +6640,7 @@ export async function saveSettings(type) { eventSource.emit(event_types.SETTINGS_UPDATED); }, error: function (jqXHR, exception) { - toastr.error('Check the server connection and reload the page to prevent data loss.', 'Settings could not be saved'); + toastr.error(t`Check the server connection and reload the page to prevent data loss.t`, t`Settings could not be saved`); console.log(exception); console.log(jqXHR); }, @@ -6906,7 +6906,7 @@ export async function displayPastChats() { const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats()); if (!data) { - toastr.error('Could not load chat data. Try reloading the page.'); + toastr.error(t`Could not load chat data. Try reloading the page.`); return; } @@ -7039,7 +7039,7 @@ export function selectRightMenuWithAnimation(selectedMenuId) { export function select_rm_info(type, charId, previousCharId = null) { if (!type) { - toastr.error('Invalid process (no \'type\')'); + toastr.error(t`Invalid process (no 'type')`); return; } if (type !== 'group_create') { @@ -7047,20 +7047,20 @@ export function select_rm_info(type, charId, previousCharId = null) { } if (type === 'char_delete') { - toastr.warning(`Character Deleted: ${displayName}`); + toastr.warning(t`Character Deleted: ${displayName}`); } if (type === 'char_create') { - toastr.success(`Character Created: ${displayName}`); + toastr.success(t`Character Created: ${displayName}`); } if (type === 'group_create') { - toastr.success('Group Created'); + toastr.success(t`Group Created`); } if (type === 'group_delete') { - toastr.warning('Group Deleted'); + toastr.warning(t`Group Deleted`); } if (type === 'char_import') { - toastr.success(`Character Imported: ${displayName}`); + toastr.success(t`Character Imported: ${displayName}`); } selectRightMenuWithAnimation('rm_characters_block'); @@ -7515,25 +7515,25 @@ export function hideSwipeButtons() { */ export async function deleteSwipe(swipeId = null) { if (swipeId && (isNaN(swipeId) || swipeId < 0)) { - toastr.warning(`Invalid swipe ID: ${swipeId + 1}`); + toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); return; } const lastMessage = chat[chat.length - 1]; if (!lastMessage || !Array.isArray(lastMessage.swipes) || !lastMessage.swipes.length) { - toastr.warning('No messages to delete swipes from.'); + toastr.warning(t`No messages to delete swipes from.`); return; } if (lastMessage.swipes.length <= 1) { - toastr.warning('Can\'t delete the last swipe.'); + toastr.warning(t`Can't delete the last swipe.`); return; } swipeId = swipeId ?? lastMessage.swipe_id; if (swipeId < 0 || swipeId >= lastMessage.swipes.length) { - toastr.warning(`Invalid swipe ID: ${swipeId + 1}`); + toastr.warning(t`Invalid swipe ID: ${swipeId + 1}`); return; } @@ -7672,7 +7672,7 @@ export function setGenerationProgress(progress) { function isHordeGenerationNotAllowed() { if (main_api == 'koboldhorde' && preset_settings == 'gui') { - toastr.error('GUI Settings preset is not supported for Horde. Please select another preset.'); + toastr.error(t`GUI Settings preset is not supported for Horde. Please select another preset.`); return true; } @@ -7721,7 +7721,7 @@ function openCharacterWorldPopup() { } $('#character_json_data').val(JSON.stringify(data)); - toastr.info('Embedded lorebook will be removed from this character.'); + toastr.info(t`Embedded lorebook will be removed from this character.`); } catch { console.error('Failed to parse character JSON data.'); } @@ -7912,11 +7912,11 @@ async function createOrEditCharacter(e) { if ($('#form_create').attr('actiontype') == 'createcharacter') { if (String($('#character_name_pole').val()).length === 0) { - toastr.error('Name is required'); + toastr.error(t`Name is required`); return; } if (is_group_generating || is_send_press) { - toastr.error('Cannot create characters while generating. Stop the request and try again.', 'Creation aborted'); + toastr.error(t`Cannot create characters while generating. Stop the request and try again.`, t`Creation aborted`); return; } try { @@ -8000,7 +8000,7 @@ async function createOrEditCharacter(e) { } catch (error) { console.error('Error creating character', error); - toastr.error('Failed to create character'); + toastr.error(t`Failed to create character`); } } else { try { @@ -8059,7 +8059,7 @@ async function createOrEditCharacter(e) { } } catch (error) { console.log(error); - toastr.error('Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.'); + toastr.error(t`Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.`); } } } @@ -8551,7 +8551,7 @@ async function selectContextCallback(args, name) { const result = fuse.search(name); if (result.length === 0) { - !quiet && toastr.warning(`Context template "${name}" not found`); + !quiet && toastr.warning(t`Context template '${name}' not found`); return ''; } @@ -8571,7 +8571,7 @@ async function selectInstructCallback(args, name) { const result = fuse.search(name); if (result.length === 0) { - !quiet && toastr.warning(`Instruct template "${name}" not found`); + !quiet && toastr.warning(t`Instruct template '${name}' not found`); return ''; } @@ -8623,7 +8623,7 @@ async function connectAPISlash(args, text) { const apiConfig = CONNECT_API_MAP[text.toLowerCase()]; if (!apiConfig) { - toastr.error(`Error: ${text} is not a valid API`); + toastr.error(t`Error: ${text} is not a valid API`); return ''; } @@ -8652,7 +8652,7 @@ async function connectAPISlash(args, text) { } const quiet = isTrueBoolean(args?.quiet); - const toast = quiet ? jQuery() : toastr.info(`API set to ${text}, trying to connect..`); + const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`); try { await waitUntilCondition(() => online_status !== 'no_connection', 10000, 100); @@ -8691,7 +8691,7 @@ export async function processDroppedFiles(files, data = new Map()) { const preservedName = data instanceof Map && data.get(file); await importCharacter(file, preservedName); } else { - toastr.warning('Unsupported file type: ' + file.name); + toastr.warning(t`Unsupported file type: ` + file.name); } } } @@ -8704,7 +8704,7 @@ export async function processDroppedFiles(files, data = new Map()) { */ async function importCharacter(file, preserveFileName = '') { if (is_group_generating || is_send_press) { - toastr.error('Cannot import characters while generating. Stop the request and try again.', 'Import aborted'); + toastr.error(t`Cannot import characters while generating. Stop the request and try again.`, t`Import aborted`); throw new Error('Cannot import character while generating'); } @@ -8731,7 +8731,7 @@ async function importCharacter(file, preserveFileName = '') { }); if (data.error) { - toastr.error('The file is likely invalid or corrupted.', 'Could not import character'); + toastr.error(t`The file is likely invalid or corrupted.`, t`Could not import character`); return; } @@ -8784,7 +8784,7 @@ async function doImpersonate(args, prompt) { await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); } catch { console.warn('Timeout waiting for generation unlock'); - toastr.warning('Cannot run /impersonate command while the reply is being generated.'); + toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`); return ''; } @@ -8846,19 +8846,19 @@ async function doDeleteChat() { async function doRenameChat(_, chatName) { if (!chatName) { - toastr.warning('Name must be provided as an argument to rename this chat.'); + toastr.warning(t`Name must be provided as an argument to rename this chat.`); return ''; } const currentChatName = getCurrentChatId(); if (!currentChatName) { - toastr.warning('No chat selected that can be renamed.'); + toastr.warning(t`No chat selected that can be renamed.`); return ''; } await renameChat(currentChatName, chatName); - toastr.success(`Successfully renamed chat to: ${chatName}`); + toastr.success(t`Successfully renamed chat to: ${chatName}`); return ''; } @@ -8973,7 +8973,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) for (const key of characterKey) { const character = characters.find(x => x.avatar == key); if (!character) { - toastr.warning(`Character ${key} not found. Skipping deletion.`); + toastr.warning(t`Character ${key} not found. Skipping deletion.`); continue; } @@ -8990,7 +8990,7 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) }); if (!response.ok) { - toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character'); + toastr.error(`${response.status} ${response.statusText}`, t`Failed to delete character`); continue; } @@ -9665,12 +9665,7 @@ jQuery(async function () { let deleteChats = false; - const confirm = await Popup.show.confirm('Delete the character?', ` - THIS IS PERMANENT!

    -
    `, { + const confirm = await Popup.show.confirm(t`Delete the character?`, await renderTemplateAsync('deleteConfirm'), { onClose: () => deleteChats = !!$('#del_char_checkbox').prop('checked'), }); if (!confirm) { diff --git a/public/scripts/PromptManager.js b/public/scripts/PromptManager.js index 551b78328..00a34c86b 100644 --- a/public/scripts/PromptManager.js +++ b/public/scripts/PromptManager.js @@ -8,6 +8,7 @@ import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; import { debounce_timeout } from './constants.js'; import { renderTemplateAsync } from './templates.js'; import { Popup } from './popup.js'; +import { t } from './i18n.js'; function debouncePromise(func, delay) { let timeoutId; @@ -455,7 +456,7 @@ class PromptManager { // Delete selected prompt from list form and close edit form this.handleDeletePrompt = async (event) => { - Popup.show.confirm('Are you sure you want to delete this prompt?', null).then((userChoice) => { + Popup.show.confirm(t`Are you sure you want to delete this prompt?`, null).then((userChoice) => { if (!userChoice) return; const promptID = document.getElementById(this.configuration.prefix + 'prompt_manager_footer_append_prompt').value; const prompt = this.getPromptById(promptID); @@ -531,7 +532,7 @@ class PromptManager { // Import prompts for the selected character this.handleImport = () => { - Popup.show.confirm('Existing prompts with the same ID will be overridden. Do you want to proceed?', null) + Popup.show.confirm(t`Existing prompts with the same ID will be overridden. Do you want to proceed?`, null) .then(userChoice => { if (!userChoice) return; @@ -552,7 +553,7 @@ class PromptManager { const data = JSON.parse(fileContent); this.import(data); } catch (err) { - toastr.error('An error occurred while importing prompts. More info available in console.'); + toastr.error(t`An error occurred while importing prompts. More info available in console.`); console.log('An error occurred while importing prompts'); console.log(err.toString()); } @@ -567,7 +568,7 @@ class PromptManager { // Restore default state of a characters prompt order this.handleCharacterReset = () => { - Popup.show.confirm('This will reset the prompt order for this character. You will not lose any prompts.', null) + Popup.show.confirm(t`This will reset the prompt order for this character. You will not lose any prompts.`, null) .then(userChoice => { if (!userChoice) return; @@ -1649,7 +1650,7 @@ class PromptManager { }; if (false === this.validateObject(controlObj, importData)) { - toastr.warning('Could not import prompts. Export failed validation.'); + toastr.warning(t`Could not import prompts. Export failed validation.`); return; } @@ -1672,7 +1673,7 @@ class PromptManager { throw new Error('Prompt order strategy not supported.'); } - toastr.success('Prompt import complete.'); + toastr.success(t`Prompt import complete.`); this.saveServiceSettings().then(() => this.render()); } diff --git a/public/scripts/authors-note.js b/public/scripts/authors-note.js index 31a5719e1..5e1cf44a3 100644 --- a/public/scripts/authors-note.js +++ b/public/scripts/authors-note.js @@ -16,6 +16,7 @@ import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { SlashCommand } from './slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; export { MODULE_NAME as NOTE_MODULE_NAME }; +import { t } from './i18n.js'; const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory @@ -37,7 +38,7 @@ const chara_note_position = { function setNoteTextCommand(_, text) { $('#extension_floating_prompt').val(text).trigger('input'); - toastr.success('Author\'s Note text updated'); + toastr.success(t`Author's Note text updated`); return ''; } @@ -45,12 +46,12 @@ function setNoteDepthCommand(_, text) { const value = Number(text); if (Number.isNaN(value)) { - toastr.error('Not a valid number'); + toastr.error(t`Not a valid number`); return; } $('#extension_floating_depth').val(Math.abs(value)).trigger('input'); - toastr.success('Author\'s Note depth updated'); + toastr.success(t`Author's Note depth updated`); return ''; } @@ -58,12 +59,12 @@ function setNoteIntervalCommand(_, text) { const value = Number(text); if (Number.isNaN(value)) { - toastr.error('Not a valid number'); + toastr.error(t`Not a valid number`); return; } $('#extension_floating_interval').val(Math.abs(value)).trigger('input'); - toastr.success('Author\'s Note frequency updated'); + toastr.success(t`Author's Note frequency updated`); return ''; } @@ -76,12 +77,12 @@ function setNotePositionCommand(_, text) { const position = validPositions[text?.trim()]; if (Number.isNaN(position)) { - toastr.error('Not a valid position'); + toastr.error(t`Not a valid position`); return; } $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input'); - toastr.info('Author\'s Note position updated'); + toastr.info(t`Author's Note position updated`); return ''; } @@ -206,7 +207,7 @@ function onExtensionFloatingCharaPromptInput() { extension_settings.note.chara.push(tempCharaNote); } else { console.log('Character author\'s note error: No avatar name key could be found.'); - toastr.error('Something went wrong. Could not save character\'s author\'s note.'); + toastr.error(t`Something went wrong. Could not save character's author's note.`); // Don't save settings if something went wrong return; @@ -397,7 +398,7 @@ function onANMenuItemClick() { //because this listener takes priority $('#options').stop().fadeOut(animation_duration); } else { - toastr.warning('Select a character before trying to use Author\'s Note', '', { timeOut: 2000 }); + toastr.warning(t`Select a character before trying to use Author's Note`, '', { timeOut: 2000 }); } } diff --git a/public/scripts/chats.js b/public/scripts/chats.js index 75ad746b9..d8f1e8dad 100644 --- a/public/scripts/chats.js +++ b/public/scripts/chats.js @@ -40,6 +40,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; import { ScraperManager } from './scrapers.js'; import { DragAndDropHandler } from './dragdrop.js'; import { renderTemplateAsync } from './templates.js'; +import { t } from './i18n.js'; /** * @typedef {Object} FileAttachment @@ -206,7 +207,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input const fileText = await converter(file); base64Data = window.btoa(unescape(encodeURIComponent(fileText))); } catch (error) { - toastr.error(String(error), 'Could not convert file'); + toastr.error(String(error), t`Could not convert file`); console.error('Could not convert file', error); } } @@ -257,7 +258,7 @@ export async function uploadFileAttachment(fileName, base64Data) { const responseData = await result.json(); return responseData.path; } catch (error) { - toastr.error(String(error), 'Could not upload file'); + toastr.error(String(error), t`Could not upload file`); console.error('Could not upload file', error); } } @@ -283,7 +284,7 @@ export async function getFileAttachment(url) { const text = await result.text(); return text; } catch (error) { - toastr.error(error, 'Could not download file'); + toastr.error(error, t`Could not download file`); console.error('Could not download file', error); } } @@ -299,13 +300,13 @@ async function validateFile(file) { const isBinary = /^[\x00-\x08\x0E-\x1F\x7F-\xFF]*$/.test(fileText); if (!isImage && file.size > fileSizeLimit) { - toastr.error(`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`); + toastr.error(t`File is too big. Maximum size is ${humanFileSize(fileSizeLimit)}.`); return false; } // If file is binary if (isBinary && !isImage && !isConvertible(file.type)) { - toastr.error('Binary files are not supported. Select a text file or image.'); + toastr.error(t`Binary files are not supported. Select a text file or image.`); return false; } @@ -521,7 +522,7 @@ async function openExternalMediaOverridesDialog() { const entityId = getCurrentEntityId(); if (!entityId) { - toastr.info('No character or group selected'); + toastr.info(t`No character or group selected`); return; } @@ -646,7 +647,7 @@ async function deleteFileFromServer(url, silent = false) { await eventSource.emit(event_types.FILE_ATTACHMENT_DELETED, url); return true; } catch (error) { - toastr.error(String(error), 'Could not delete file'); + toastr.error(String(error), t`Could not delete file`); console.error('Could not delete file', error); return false; } @@ -1054,7 +1055,7 @@ async function openAttachmentManager() { const selectedAttachments = document.querySelectorAll('.attachmentListItemCheckboxContainer .attachmentListItemCheckbox:checked'); if (selectedAttachments.length === 0) { - toastr.info('No attachments selected.', 'Data Bank'); + toastr.info(t`No attachments selected.`, t`Data Bank`); return; } @@ -1168,7 +1169,7 @@ async function runScraper(scraperId, target, callback) { if (files.length === 0) { console.warn('Scraping returned no files'); - toastr.info('No files were scraped.', 'Data Bank'); + toastr.info(t`No files were scraped.`, t`Data Bank`); return; } @@ -1176,12 +1177,12 @@ async function runScraper(scraperId, target, callback) { await uploadFileAttachmentToServer(file, target); } - toastr.success(`Scraped ${files.length} files from ${scraperId} to ${target}.`, 'Data Bank'); + toastr.success(t`Scraped ${files.length} files from ${scraperId} to ${target}.`, t`Data Bank`); callback(); } catch (error) { console.error('Scraping failed', error); - toastr.error('Check browser console for details.', 'Scraping failed'); + toastr.error(t`Check browser console for details.`, t`Scraping failed`); } } @@ -1208,7 +1209,7 @@ export async function uploadFileAttachmentToServer(file, target) { const fileText = await converter(file); base64Data = window.btoa(unescape(encodeURIComponent(fileText))); } catch (error) { - toastr.error(String(error), 'Could not convert file'); + toastr.error(String(error), t`Could not convert file`); console.error('Could not convert file', error); } } else { diff --git a/public/scripts/extensions/regex/editor.html b/public/scripts/extensions/regex/editor.html index 5552504e1..aa207ffd9 100644 --- a/public/scripts/extensions/regex/editor.html +++ b/public/scripts/extensions/regex/editor.html @@ -132,7 +132,7 @@ Ephemerality -