Merge pull request #2291 from Yokayo/staging

Localizable prompt manager
This commit is contained in:
Cohee
2024-05-24 22:32:04 +03:00
committed by GitHub
9 changed files with 86 additions and 74 deletions

View File

@@ -19,7 +19,7 @@
#completion_prompt_manager #completion_prompt_manager_list li { #completion_prompt_manager #completion_prompt_manager_list li {
display: grid; display: grid;
grid-template-columns: 4fr 80px 40px; grid-template-columns: 4fr 80px 45px;
margin-bottom: 0.5em; margin-bottom: 0.5em;
width: 100% width: 100%
} }

View File

@@ -1750,13 +1750,13 @@
</div> </div>
</label> </label>
<div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom"> <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom">
<label for="openai_inline_image_quality"> <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
Inline Image Quality Inline Image Quality
</label> </label>
<select id="openai_inline_image_quality"> <select id="openai_inline_image_quality">
<option value="auto">Auto</option> <option data-i18n="openai_inline_image_quality_auto" value="auto">Auto</option>
<option value="low">Low</option> <option data-i18n="openai_inline_image_quality_low" value="low">Low</option>
<option value="high">High</option> <option data-i18n="openai_inline_image_quality_high" value="high">High</option>
</select> </select>
</div> </div>
</div> </div>
@@ -4511,8 +4511,8 @@
</div> </div>
</div> </div>
<a id="chartokenwarning" class="right_menu_button fa-solid fa-triangle-exclamation" href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank" title="About Token 'Limits'"></a> <a id="chartokenwarning" class="right_menu_button fa-solid fa-triangle-exclamation" href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank" title="About Token 'Limits'"></a>
<i title="Click for stats!" class="fa-solid fa-ranking-star right_menu_button rm_stats_button"></i> <i title="Click for stats!" data-i18n="[title]Click for stats!" class="fa-solid fa-ranking-star right_menu_button rm_stats_button"></i>
<i title="Toggle character info panel" id="hideCharPanelAvatarButton" class="fa-solid fa-eye right_menu_button"></i> <i title="Toggle character info panel" data-i18n="[title]Toggle character info panel" id="hideCharPanelAvatarButton" class="fa-solid fa-eye right_menu_button"></i>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -381,7 +381,7 @@
"Delete": "Удалить", "Delete": "Удалить",
"Cancel": "Отменить", "Cancel": "Отменить",
"Advanced Defininitions": "Расширенное описание", "Advanced Defininitions": "Расширенное описание",
"Personality summary": "Сводка по личности", "Personality summary": "Резюме по личности",
"A brief description of the personality": "Краткое описание личности", "A brief description of the personality": "Краткое описание личности",
"Scenario": "Сценарий", "Scenario": "Сценарий",
"Circumstances and context of the dialogue": "Обстоятельства и контекст диалога", "Circumstances and context of the dialogue": "Обстоятельства и контекст диалога",
@@ -627,7 +627,7 @@
"Most chats": "Больше всего чатов", "Most chats": "Больше всего чатов",
"Least chats": "Меньше всего чатов", "Least chats": "Меньше всего чатов",
"Back": "Назад", "Back": "Назад",
"Prompt Overrides": "Индивидуальный промпт", "Prompt Overrides": "Индивидуальные промпты",
"(For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct Mode)": "(для API OpenAI/Claude/Scale, Window/OpenRouter, а также режима Instruct)", "(For OpenAI/Claude/Scale APIs, Window/OpenRouter, and Instruct Mode)": "(для API OpenAI/Claude/Scale, Window/OpenRouter, а также режима Instruct)",
"Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы вставить соответствующий промпт из системных настроек", "Insert {{original}} into either box to include the respective default prompt from system settings.": "Введите {{original}} в любое поле, чтобы вставить соответствующий промпт из системных настроек",
"Main Prompt": "Основной промпт", "Main Prompt": "Основной промпт",
@@ -872,7 +872,7 @@
"Assistant Prefill": "Префилл для ассистента", "Assistant Prefill": "Префилл для ассистента",
"Start Claude's answer with...": "Начать ответ Клода с...", "Start Claude's answer with...": "Начать ответ Клода с...",
"Use system prompt (Claude 2.1+ only)": "Использовать системный промпт (только Claude 2.1+)", "Use system prompt (Claude 2.1+ only)": "Использовать системный промпт (только Claude 2.1+)",
"Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Отправлять системный промпт для поддерживаемых моделей. Если отключено, сообщение пользователя добавляется в начало промпта.", "Send the system prompt for supported models. If disabled, the user message is added to the beginning of the prompt.": "Отправлять системный промпт для поддерживаемых моделей. Если отключено, в начало промпта добавляется сообщение пользователя.",
"Prompts": "Промпты", "Prompts": "Промпты",
"Total Tokens:": "Всего токенов:", "Total Tokens:": "Всего токенов:",
"Insert prompt": "Вставить промпт", "Insert prompt": "Вставить промпт",
@@ -1273,5 +1273,15 @@
"To change your user avatar, use the buttons below or select a default persona in the Persona Management menu.": "Чтобы сменить аватарку, используйте кнопки ниже, либо выберите персону по умолчанию в меню управления персоной.", "To change your user avatar, use the buttons below or select a default persona in the Persona Management menu.": "Чтобы сменить аватарку, используйте кнопки ниже, либо выберите персону по умолчанию в меню управления персоной.",
"These characters are the winners of character design contests and have outstandable quality.": "Персонажи наивысшего качества, одержавшие победу в конкурсе персонажей.", "These characters are the winners of character design contests and have outstandable quality.": "Персонажи наивысшего качества, одержавшие победу в конкурсе персонажей.",
"Featured Characters": "Рекомендуемые персонажи", "Featured Characters": "Рекомендуемые персонажи",
"These characters are the finalists of character design contests and have remarkable quality.": "Персонажи отличного качества, финалисты конкурса персонажей." "These characters are the finalists of character design contests and have remarkable quality.": "Персонажи отличного качества, финалисты конкурса персонажей.",
"Inline Image Quality": "Качество inline-изображений",
"openai_inline_image_quality_auto": "Автоопределение",
"openai_inline_image_quality_low": "Низкое",
"openai_inline_image_quality_high": "Высокое",
"Assistant Impersonation Prefill": "Префилл для ассистента при перевоплощении",
"Hide Chat Avatars": "Не показывать аватарки в чате",
"Hide avatars in chat messages.": "Скрыть аватарки сбоку от сообщений в чате",
"Flat": "Стандартный",
"Toggle character info panel": "Показать / скрыть инфо-панель",
"(For Chat Completion and Instruct Mode)": "(для Chat Completion и режима Instruct)"
} }

View File

@@ -6,6 +6,7 @@ import { Message, TokenHandler } from './openai.js';
import { power_user } from './power-user.js'; import { power_user } from './power-user.js';
import { debounce, waitUntilCondition, escapeHtml } from './utils.js'; import { debounce, waitUntilCondition, escapeHtml } from './utils.js';
import { debounce_timeout } from './constants.js'; import { debounce_timeout } from './constants.js';
import { renderTemplateAsync } from './templates.js';
function debouncePromise(func, delay) { function debouncePromise(func, delay) {
let timeoutId; let timeoutId;
@@ -250,7 +251,7 @@ class PromptManager {
this.error = null; this.error = null;
/** Dry-run for generate, must return a promise */ /** Dry-run for generate, must return a promise */
this.tryGenerate = () => { }; this.tryGenerate = async () => { };
/** Called to persist the configuration, must return a promise */ /** Called to persist the configuration, must return a promise */
this.saveServiceSettings = () => { }; this.saveServiceSettings = () => { };
@@ -695,23 +696,23 @@ class PromptManager {
if ('character' === this.configuration.promptOrder.strategy && null === this.activeCharacter) return; if ('character' === this.configuration.promptOrder.strategy && null === this.activeCharacter) return;
this.error = null; this.error = null;
waitUntilCondition(() => !is_send_press && !is_group_generating, 1024 * 1024, 100).then(() => { waitUntilCondition(() => !is_send_press && !is_group_generating, 1024 * 1024, 100).then(async () => {
if (true === afterTryGenerate) { if (true === afterTryGenerate) {
// Executed during dry-run for determining context composition // Executed during dry-run for determining context composition
this.profileStart('filling context'); this.profileStart('filling context');
this.tryGenerate().finally(() => { this.tryGenerate().finally(async () => {
this.profileEnd('filling context'); this.profileEnd('filling context');
this.profileStart('render'); this.profileStart('render');
this.renderPromptManager(); await this.renderPromptManager();
this.renderPromptManagerListItems(); await this.renderPromptManagerListItems();
this.makeDraggable(); this.makeDraggable();
this.profileEnd('render'); this.profileEnd('render');
}); });
} else { } else {
// Executed during live communication // Executed during live communication
this.profileStart('render'); this.profileStart('render');
this.renderPromptManager(); await this.renderPromptManager();
this.renderPromptManagerListItems(); await this.renderPromptManagerListItems();
this.makeDraggable(); this.makeDraggable();
this.profileEnd('render'); this.profileEnd('render');
} }
@@ -1338,7 +1339,7 @@ class PromptManager {
/** /**
* Empties, then re-assembles the container containing the prompt list. * Empties, then re-assembles the container containing the prompt list.
*/ */
renderPromptManager() { async renderPromptManager() {
let selectedPromptIndex = 0; let selectedPromptIndex = 0;
const existingAppendSelect = document.getElementById(`${this.configuration.prefix}prompt_manager_footer_append_prompt`); const existingAppendSelect = document.getElementById(`${this.configuration.prefix}prompt_manager_footer_append_prompt`);
if (existingAppendSelect instanceof HTMLSelectElement) { if (existingAppendSelect instanceof HTMLSelectElement) {
@@ -1349,24 +1350,14 @@ class PromptManager {
const errorDiv = ` const errorDiv = `
<div class="${this.configuration.prefix}prompt_manager_error"> <div class="${this.configuration.prefix}prompt_manager_error">
<span class="fa-solid tooltip fa-triangle-exclamation text_danger"></span> ${this.error} <span class="fa-solid tooltip fa-triangle-exclamation text_danger"></span> ${DOMPurify.sanitize(this.error)}
</div> </div>
`; `;
const totalActiveTokens = this.tokenUsage; const totalActiveTokens = this.tokenUsage;
promptManagerDiv.insertAdjacentHTML('beforeend', ` const headerHtml = await renderTemplateAsync('promptManagerHeader', { error: this.error, errorDiv, prefix: this.configuration.prefix, totalActiveTokens });
<div class="range-block"> promptManagerDiv.insertAdjacentHTML('beforeend', headerHtml);
${this.error ? errorDiv : ''}
<div class="${this.configuration.prefix}prompt_manager_header">
<div class="${this.configuration.prefix}prompt_manager_header_advanced">
<span data-i18n="Prompts">Prompts</span>
</div>
<div>Total Tokens: ${totalActiveTokens} </div>
</div>
<ul id="${this.configuration.prefix}prompt_manager_list" class="text_pole"></ul>
</div>
`);
this.listElement = promptManagerDiv.querySelector(`#${this.configuration.prefix}prompt_manager_list`); this.listElement = promptManagerDiv.querySelector(`#${this.configuration.prefix}prompt_manager_list`);
@@ -1384,22 +1375,9 @@ class PromptManager {
selectedPromptIndex = 0; selectedPromptIndex = 0;
} }
const footerHtml = `
<div class="${this.configuration.prefix}prompt_manager_footer">
<select id="${this.configuration.prefix}prompt_manager_footer_append_prompt" class="text_pole" name="append-prompt">
${promptsHtml}
</select>
<a class="menu_button fa-chain fa-solid" title="Insert prompt" data-i18n="[title]Insert prompt"></a>
<a class="caution menu_button fa-x fa-solid" title="Delete prompt" data-i18n="[title]Delete prompt"></a>
<a class="menu_button fa-file-import fa-solid" id="prompt-manager-import" title="Import a prompt list" data-i18n="[title]Import a prompt list"></a>
<a class="menu_button fa-file-export fa-solid" id="prompt-manager-export" title="Export this prompt list" data-i18n="[title]Export this prompt list"></a>
<a class="menu_button fa-undo fa-solid" id="prompt-manager-reset-character" title="Reset current character" data-i18n="[title]Reset current character"></a>
<a class="menu_button fa-plus-square fa-solid" title="New prompt" data-i18n="[title]New prompt"></a>
</div>
`;
const rangeBlockDiv = promptManagerDiv.querySelector('.range-block'); const rangeBlockDiv = promptManagerDiv.querySelector('.range-block');
const headerDiv = promptManagerDiv.querySelector('.completion_prompt_manager_header'); const headerDiv = promptManagerDiv.querySelector('.completion_prompt_manager_header');
const footerHtml = await renderTemplateAsync('promptManagerFooter', { promptsHtml, prefix: this.configuration.prefix });
headerDiv.insertAdjacentHTML('afterend', footerHtml); headerDiv.insertAdjacentHTML('afterend', footerHtml);
rangeBlockDiv.querySelector('#prompt-manager-reset-character').addEventListener('click', this.handleCharacterReset); rangeBlockDiv.querySelector('#prompt-manager-reset-character').addEventListener('click', this.handleCharacterReset);
@@ -1410,23 +1388,9 @@ class PromptManager {
footerDiv.querySelector('select').selectedIndex = selectedPromptIndex; footerDiv.querySelector('select').selectedIndex = selectedPromptIndex;
// Add prompt export dialogue and options // Add prompt export dialogue and options
const exportForCharacter = `
<div class="row">
<a class="export-promptmanager-prompts-character list-group-item" data-i18n="Export for character">Export for character</a>
<span class="tooltip fa-solid fa-info-circle" title="Export prompts for this character, including their order."></span>
</div>`;
const exportPopup = `
<div id="prompt-manager-export-format-popup" class="list-group">
<div class="prompt-manager-export-format-popup-flex">
<div class="row">
<a class="export-promptmanager-prompts-full list-group-item" data-i18n="Export all">Export all</a>
<span class="tooltip fa-solid fa-info-circle" title="Export all your prompts to a file"></span>
</div>
${'global' === this.configuration.promptOrder.strategy ? '' : exportForCharacter}
</div>
</div>
`;
const exportForCharacter = await renderTemplateAsync('promptManagerExportForCharacter');
const exportPopup = await renderTemplateAsync('promptManagerExportPopup', { isGlobalStrategy: 'global' === this.configuration.promptOrder.strategy, exportForCharacter });
rangeBlockDiv.insertAdjacentHTML('beforeend', exportPopup); rangeBlockDiv.insertAdjacentHTML('beforeend', exportPopup);
// Destroy previous popper instance if it exists // Destroy previous popper instance if it exists
@@ -1460,7 +1424,7 @@ class PromptManager {
/** /**
* Empties, then re-assembles the prompt list * Empties, then re-assembles the prompt list
*/ */
renderPromptManagerListItems() { async renderPromptManagerListItems() {
if (!this.serviceSettings.prompts) return; if (!this.serviceSettings.prompts) return;
const promptManagerList = this.listElement; const promptManagerList = this.listElement;
@@ -1468,16 +1432,7 @@ class PromptManager {
const { prefix } = this.configuration; const { prefix } = this.configuration;
let listItemHtml = ` let listItemHtml = await renderTemplateAsync('promptManagerListHeader', { prefix });
<li class="${prefix}prompt_manager_list_head">
<span data-i18n="Name">Name</span>
<span></span>
<span class="prompt_manager_prompt_tokens" data-i18n="Tokens">Tokens</span>
</li>
<li class="${prefix}prompt_manager_list_separator">
<hr>
</li>
`;
this.getPromptsForCharacter(this.activeCharacter).forEach(prompt => { this.getPromptsForCharacter(this.activeCharacter).forEach(prompt => {
if (!prompt) return; if (!prompt) return;
@@ -1602,7 +1557,7 @@ class PromptManager {
data: data, data: data,
}; };
const serializedObject = JSON.stringify(promptExport); const serializedObject = JSON.stringify(promptExport, null, 4);
const blob = new Blob([serializedObject], { type: 'application/json' }); const blob = new Blob([serializedObject], { type: 'application/json' });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const downloadLink = document.createElement('a'); const downloadLink = document.createElement('a');

View File

@@ -0,0 +1,4 @@
<div class="row">
<a class="export-promptmanager-prompts-character list-group-item" data-i18n="Export for character">Export for character</a>
<span class="tooltip fa-solid fa-info-circle" data-i18n="[title]Export prompts for this character, including their order." title="Export prompts for this character, including their order."></span>
</div>

View File

@@ -0,0 +1,12 @@
<div id="prompt-manager-export-format-popup" class="list-group">
<div class="prompt-manager-export-format-popup-flex">
<div class="row">
<a class="export-promptmanager-prompts-full list-group-item" data-i18n="Export all">Export all</a>
<span class="tooltip fa-solid fa-info-circle" data-i18n="[title]Export all your prompts to a file" title="Export all your prompts to a file"></span>
</div>
{{#if isGlobalStrategy}}
{{else}}
{{{exportForCharacter}}}
{{/if}}
</div>
</div>

View File

@@ -0,0 +1,11 @@
<div class="{{prefix}}prompt_manager_footer">
<select id="{{prefix}}prompt_manager_footer_append_prompt" class="text_pole" name="append-prompt">
{{{promptsHtml}}}
</select>
<a class="menu_button fa-chain fa-solid" title="Insert prompt" data-i18n="[title]Insert prompt"></a>
<a class="caution menu_button fa-x fa-solid" title="Delete prompt" data-i18n="[title]Delete prompt"></a>
<a class="menu_button fa-file-import fa-solid" id="prompt-manager-import" title="Import a prompt list" data-i18n="[title]Import a prompt list"></a>
<a class="menu_button fa-file-export fa-solid" id="prompt-manager-export" title="Export this prompt list" data-i18n="[title]Export this prompt list"></a>
<a class="menu_button fa-undo fa-solid" id="prompt-manager-reset-character" title="Reset current character" data-i18n="[title]Reset current character"></a>
<a class="menu_button fa-plus-square fa-solid" title="New prompt" data-i18n="[title]New prompt"></a>
</div>

View File

@@ -0,0 +1,12 @@
<div class="range-block">
{{#if error}}
{{{errorDiv}}}
{{/if}}
<div class="{{prefix}}prompt_manager_header">
<div class="{{prefix}}prompt_manager_header_advanced">
<span data-i18n="Prompts">Prompts</span>
</div>
<div><span data-i18n="Total Tokens:">Total Tokens:</span> {{totalActiveTokens}} </div>
</div>
<ul id="{{prefix}}prompt_manager_list" class="text_pole"></ul>
</div>

View File

@@ -0,0 +1,8 @@
<li class="{{prefix}}prompt_manager_list_head">
<span data-i18n="Name">Name</span>
<span></span>
<span class="prompt_manager_prompt_tokens" data-i18n="Tokens;prompt_manager_tokens">Tokens</span>
</li>
<li class="{{prefix}}prompt_manager_list_separator">
<hr>
</li>