mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Translate impersonate and streaming responses. 4-state auto-mode for translation
This commit is contained in:
@ -417,6 +417,7 @@ export const event_types = {
|
|||||||
MESSAGE_SWIPED: 'message_swiped',
|
MESSAGE_SWIPED: 'message_swiped',
|
||||||
MESSAGE_SENT: 'message_sent',
|
MESSAGE_SENT: 'message_sent',
|
||||||
MESSAGE_RECEIVED: 'message_received',
|
MESSAGE_RECEIVED: 'message_received',
|
||||||
|
IMPERSONATE_READY: 'impersonate_ready',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const eventSource = new EventEmitter();
|
export const eventSource = new EventEmitter();
|
||||||
@ -1667,6 +1668,10 @@ class StreamingProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
playMessageSound();
|
playMessageSound();
|
||||||
|
|
||||||
|
const eventType = this.type !== 'impersonate' ? event_types.MESSAGE_RECEIVED : event_types.IMPERSONATE_READY;
|
||||||
|
const eventData = this.type !== 'impersonate' ? this.messageId : text;
|
||||||
|
eventSource.emit(eventType, eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
onErrorStreaming() {
|
onErrorStreaming() {
|
||||||
@ -1831,7 +1836,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
|||||||
sendSystemMessage(system_message_types.GENERIC, ' ', { bias: messageBias });
|
sendSystemMessage(system_message_types.GENERIC, ' ', { bias: messageBias });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sendMessageAsUser(textareaText, messageBias);
|
await sendMessageAsUser(textareaText, messageBias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
@ -2384,6 +2389,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
|||||||
if (isImpersonate) {
|
if (isImpersonate) {
|
||||||
$('#send_textarea').val(getMessage).trigger('input');
|
$('#send_textarea').val(getMessage).trigger('input');
|
||||||
generatedPromtCache = "";
|
generatedPromtCache = "";
|
||||||
|
eventSource.emit(event_types.IMPERSONATE_READY, getMessage);
|
||||||
}
|
}
|
||||||
else if (type == 'quiet') {
|
else if (type == 'quiet') {
|
||||||
resolve(getMessage);
|
resolve(getMessage);
|
||||||
@ -2395,15 +2401,14 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
|||||||
else {
|
else {
|
||||||
({ type, getMessage } = saveReply('appendFinal', getMessage, this_mes_is_name, title));
|
({ type, getMessage } = saveReply('appendFinal', getMessage, this_mes_is_name, title));
|
||||||
}
|
}
|
||||||
|
eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||||
}
|
}
|
||||||
activateSendButtons();
|
activateSendButtons();
|
||||||
|
|
||||||
if (type !== 'quiet') {
|
if (type !== 'quiet') {
|
||||||
playMessageSound();
|
playMessageSound();
|
||||||
eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
generate_loop_counter = 0;
|
generate_loop_counter = 0;
|
||||||
} else {
|
} else {
|
||||||
++generate_loop_counter;
|
++generate_loop_counter;
|
||||||
@ -2522,7 +2527,7 @@ export function replaceBiasMarkup(str) {
|
|||||||
return (str ?? '').replace(/{{(\*?.*\*?)}}/g, '');
|
return (str ?? '').replace(/{{(\*?.*\*?)}}/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessageAsUser(textareaText, messageBias) {
|
async function sendMessageAsUser(textareaText, messageBias) {
|
||||||
chat[chat.length] = {};
|
chat[chat.length] = {};
|
||||||
chat[chat.length - 1]['name'] = name1;
|
chat[chat.length - 1]['name'] = name1;
|
||||||
chat[chat.length - 1]['is_user'] = true;
|
chat[chat.length - 1]['is_user'] = true;
|
||||||
@ -2537,7 +2542,9 @@ function sendMessageAsUser(textareaText, messageBias) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addOneMessage(chat[chat.length - 1]);
|
addOneMessage(chat[chat.length - 1]);
|
||||||
eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
// Wait for all handlers to finish before continuing with the prompt
|
||||||
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
|
console.log('message sent as user');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMaxContextSize() {
|
function getMaxContextSize() {
|
||||||
|
@ -48,7 +48,7 @@ EventEmitter.prototype.removeListener = function (event, listener) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EventEmitter.prototype.emit = function (event) {
|
EventEmitter.prototype.emit = async function (event) {
|
||||||
var i, listeners, length, args = [].slice.call(arguments, 1);
|
var i, listeners, length, args = [].slice.call(arguments, 1);
|
||||||
|
|
||||||
if (typeof this.events[event] === 'object') {
|
if (typeof this.events[event] === 'object') {
|
||||||
@ -56,7 +56,7 @@ EventEmitter.prototype.emit = function (event) {
|
|||||||
length = listeners.length;
|
length = listeners.length;
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
listeners[i].apply(this, args);
|
await listeners[i].apply(this, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -68,4 +68,4 @@ EventEmitter.prototype.once = function (event, listener) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { EventEmitter }
|
export { EventEmitter }
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
import { eventSource, event_types, getRequestHeaders, messageFormatting, saveSettingsDebounced } from "../../../script.js";
|
import { eventSource, event_types, getRequestHeaders, messageFormatting, saveSettingsDebounced } from "../../../script.js";
|
||||||
import { extension_settings, getContext } from "../../extensions.js";
|
import { extension_settings, getContext } from "../../extensions.js";
|
||||||
|
|
||||||
|
const autoModeOptions = {
|
||||||
|
NONE: 'none',
|
||||||
|
RESPONSES: 'responses',
|
||||||
|
INPUT: 'inputs',
|
||||||
|
BOTH: 'both',
|
||||||
|
};
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
target_language: 'en',
|
target_language: 'en',
|
||||||
|
internal_language: 'en',
|
||||||
provider: 'google',
|
provider: 'google',
|
||||||
auto: false,
|
auto_mode: autoModeOptions.NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
const languageCodes = {
|
const languageCodes = {
|
||||||
@ -115,13 +123,20 @@ const languageCodes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
if (Object.keys(extension_settings.translate).length === 0) {
|
for (const key in defaultSettings) {
|
||||||
Object.assign(extension_settings.translate, defaultSettings);
|
if (!extension_settings.translate.hasOwnProperty(key)) {
|
||||||
|
extension_settings.translate[key] = defaultSettings[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true);
|
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true);
|
||||||
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true);
|
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true);
|
||||||
$('#translation_auto').prop('checked', extension_settings.translate.auto);
|
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function translateImpersonate(text) {
|
||||||
|
const translatedText = await translate(text, extension_settings.translate.target_language);
|
||||||
|
$("#send_textarea").val(translatedText);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function translateIncomingMessage(messageId) {
|
async function translateIncomingMessage(messageId) {
|
||||||
@ -137,7 +152,7 @@ async function translateIncomingMessage(messageId) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translation = await translate(message.mes);
|
const translation = await translate(message.mes, extension_settings.translate.target_language);
|
||||||
message.extra.display_text = translation;
|
message.extra.display_text = translation;
|
||||||
|
|
||||||
$(`#chat .mes[mesid="${messageId}"] .mes_text`).html(messageFormatting(translation, message.name, message.is_system, message.is_user));
|
$(`#chat .mes[mesid="${messageId}"] .mes_text`).html(messageFormatting(translation, message.name, message.is_system, message.is_user));
|
||||||
@ -145,11 +160,11 @@ async function translateIncomingMessage(messageId) {
|
|||||||
context.saveChat();
|
context.saveChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function translateProviderGoogle(text) {
|
async function translateProviderGoogle(text, lang) {
|
||||||
const response = await fetch('/google_translate', {
|
const response = await fetch('/google_translate', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ text: text, lang: extension_settings.translate.target_language }),
|
body: JSON.stringify({ text: text, lang: lang }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@ -160,11 +175,11 @@ async function translateProviderGoogle(text) {
|
|||||||
throw new Error(response.statusText);
|
throw new Error(response.statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function translate(text) {
|
async function translate(text, lang) {
|
||||||
try {
|
try {
|
||||||
switch (extension_settings.translate.provider) {
|
switch (extension_settings.translate.provider) {
|
||||||
case 'google':
|
case 'google':
|
||||||
return await translateProviderGoogle(text);
|
return await translateProviderGoogle(text, lang);
|
||||||
default:
|
default:
|
||||||
console.error('Unknown translation provider', extension_settings.translate.provider);
|
console.error('Unknown translation provider', extension_settings.translate.provider);
|
||||||
return text;
|
return text;
|
||||||
@ -176,7 +191,31 @@ async function translate(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function translateOutgoingMessage(messageId) {
|
async function translateOutgoingMessage(messageId) {
|
||||||
alert('translateOutgoingMessage', messageId);
|
const context = getContext();
|
||||||
|
const message = context.chat[messageId];
|
||||||
|
|
||||||
|
if (typeof message.extra !== 'object') {
|
||||||
|
message.extra = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalText = message.mes;
|
||||||
|
message.extra.display_text = originalText;
|
||||||
|
$(`#chat .mes[mesid="${messageId}"] .mes_text`).html(messageFormatting(originalText, message.name, message.is_system, message.is_user));
|
||||||
|
message.mes = await translate(originalText, extension_settings.translate.internal_language);
|
||||||
|
|
||||||
|
console.log('translateOutgoingMessage', messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldTranslate(types) {
|
||||||
|
return types.includes(extension_settings.translate.auto_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEventHandler(translateFunction, shouldTranslateFunction) {
|
||||||
|
return async (data) => {
|
||||||
|
if (shouldTranslateFunction()) {
|
||||||
|
await translateFunction(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
jQuery(() => {
|
jQuery(() => {
|
||||||
@ -188,10 +227,13 @@ jQuery(() => {
|
|||||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-drawer-content">
|
<div class="inline-drawer-content">
|
||||||
<label for="translation_auto" class="checkbox_label">
|
<label for="translation_auto_mode" class="checkbox_label">Auto-mode</label>
|
||||||
<input type="checkbox" id="translation_auto" />
|
<select id="translation_auto_mode">
|
||||||
Auto-mode
|
<option value="none">None</option>
|
||||||
</label>
|
<option value="responses">Translate responses</option>
|
||||||
|
<option value="inputs">Translate inputs</option>
|
||||||
|
<option value="both">Translate both</option>
|
||||||
|
</select>
|
||||||
<label for="translation_provider">Provider</label>
|
<label for="translation_provider">Provider</label>
|
||||||
<select id="translation_provider" name="provider">
|
<select id="translation_provider" name="provider">
|
||||||
<option value="google">Google</option>
|
<option value="google">Google</option>
|
||||||
@ -203,33 +245,13 @@ jQuery(() => {
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
$('#extensions_settings').append(html);
|
$('#extensions_settings').append(html);
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(languageCodes)) {
|
for (const [key, value] of Object.entries(languageCodes)) {
|
||||||
$('#translation_target_language').append(`<option value="${value}">${key}</option>`);
|
$('#translation_target_language').append(`<option value="${value}">${key}</option>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
$('#translation_auto_mode').on('change', (event) => {
|
||||||
eventSource.on(event_types.MESSAGE_RECEIVED, async (messageId) => {
|
extension_settings.translate.auto_mode = event.target.value;
|
||||||
if (!extension_settings.translate.auto) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await translateIncomingMessage(messageId);
|
|
||||||
});
|
|
||||||
eventSource.on(event_types.MESSAGE_SWIPED, async (messageId) => {
|
|
||||||
if (!extension_settings.translate.auto) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await translateIncomingMessage(messageId);
|
|
||||||
});
|
|
||||||
eventSource.on(event_types.MESSAGE_SENT, async (messageId) => {
|
|
||||||
if (!extension_settings.translate.auto) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await translateOutgoingMessage(messageId);
|
|
||||||
});
|
|
||||||
$('#translation_auto').on('input', (event) => {
|
|
||||||
extension_settings.translate.auto = event.target.checked;
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
$('#translation_provider').on('change', (event) => {
|
$('#translation_provider').on('change', (event) => {
|
||||||
@ -244,5 +266,20 @@ jQuery(() => {
|
|||||||
const messageId = $(this).closest('.mes').attr('mesid');
|
const messageId = $(this).closest('.mes').attr('mesid');
|
||||||
translateIncomingMessage(messageId);
|
translateIncomingMessage(messageId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
const incomingTypes = [autoModeOptions.RESPONSES, autoModeOptions.BOTH];
|
||||||
|
const outgoingTypes = [autoModeOptions.INPUT, autoModeOptions.BOTH];
|
||||||
|
|
||||||
|
const handleIncomingMessage = createEventHandler(translateIncomingMessage, () => shouldTranslate(incomingTypes));
|
||||||
|
const handleOutgoingMessage = createEventHandler(translateOutgoingMessage, () => shouldTranslate(outgoingTypes));
|
||||||
|
const handleImpersonateReady = createEventHandler(translateImpersonate, () => shouldTranslate(incomingTypes));
|
||||||
|
|
||||||
|
eventSource.on(event_types.MESSAGE_RECEIVED, handleIncomingMessage);
|
||||||
|
eventSource.on(event_types.MESSAGE_SWIPED, handleIncomingMessage);
|
||||||
|
eventSource.on(event_types.MESSAGE_SENT, handleOutgoingMessage);
|
||||||
|
eventSource.on(event_types.IMPERSONATE_READY, handleImpersonateReady);
|
||||||
|
|
||||||
document.body.classList.add('translate');
|
document.body.classList.add('translate');
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,8 @@ import {
|
|||||||
chat,
|
chat,
|
||||||
chat_metadata,
|
chat_metadata,
|
||||||
default_avatar,
|
default_avatar,
|
||||||
|
eventSource,
|
||||||
|
event_types,
|
||||||
extractMessageBias,
|
extractMessageBias,
|
||||||
getThumbnailUrl,
|
getThumbnailUrl,
|
||||||
replaceBiasMarkup,
|
replaceBiasMarkup,
|
||||||
@ -31,7 +33,7 @@ class SlashCommandParser {
|
|||||||
if ([command, ...aliases].some(x => this.commands.hasOwnProperty(x))) {
|
if ([command, ...aliases].some(x => this.commands.hasOwnProperty(x))) {
|
||||||
console.trace('WARN: Duplicate slash command registered!');
|
console.trace('WARN: Duplicate slash command registered!');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.commands[command] = fnObj;
|
this.commands[command] = fnObj;
|
||||||
|
|
||||||
if (Array.isArray(aliases)) {
|
if (Array.isArray(aliases)) {
|
||||||
@ -105,7 +107,7 @@ function setNarratorName(_, text) {
|
|||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessageAs(_, text) {
|
async function sendMessageAs(_, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -151,10 +153,11 @@ function sendMessageAs(_, text) {
|
|||||||
|
|
||||||
chat.push(message);
|
chat.push(message);
|
||||||
addOneMessage(message);
|
addOneMessage(message);
|
||||||
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendNarratorMessage(_, text) {
|
async function sendNarratorMessage(_, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -180,6 +183,7 @@ function sendNarratorMessage(_, text) {
|
|||||||
|
|
||||||
chat.push(message);
|
chat.push(message);
|
||||||
addOneMessage(message);
|
addOneMessage(message);
|
||||||
|
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||||
saveChatConditional();
|
saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,4 +242,4 @@ function executeSlashCommands(text) {
|
|||||||
const newText = lines.filter(x => linesToRemove.indexOf(x) === -1).join('\n');
|
const newText = lines.filter(x => linesToRemove.indexOf(x) === -1).join('\n');
|
||||||
|
|
||||||
return { interrupt, newText };
|
return { interrupt, newText };
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user