Merge branch 'SillyTavern:release' into enhancement-make-buttons-scrollable
Signed| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "sillytavern", |
| 3 | 3 | "version": "1.12.67", |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "sillytavern", |
| 9 | 9 | "version": "1.12.67", |
| 10 | 10 | "hasInstallScript": true, |
| 11 | 11 | "license": "AGPL-3.0", |
| 12 | 12 | "dependencies": { |
| @@ -24,6 +24,7 @@ | ||
| 24 | 24 | "csrf-csrf": "^2.2.3", |
| 25 | 25 | "express": "^4.21.0", |
| 26 | 26 | "form-data": "^4.0.0", |
| 27 | + "google-translate-api-browser": "^3.0.1", | |
| 27 | 28 | "google-translate-api-x": "^10.7.1", |
| 28 | 29 | "helmet": "^7.1.0", |
| 29 | 30 | "html-entities": "^2.5.2", |
| @@ -3693,6 +3694,12 @@ | ||
| 3693 | 3694 | "url": "https://github.com/sponsors/sindresorhus" |
| 3694 | 3695 | } |
| 3695 | 3696 | }, |
| 3697 | + "node_modules/google-translate-api-browser": { | |
| 3698 | + "version": "3.0.1", | |
| 3699 | + "resolved": "https://registry.npmjs.org/google-translate-api-browser/-/google-translate-api-browser-3.0.1.tgz", | |
| 3700 | + "integrity": "sha512-KTLodkyGBWMK9IW6QIeJ2zCuju4Z0CLpbkADKo+yLhbSTD4l+CXXpQ/xaynGVAzeBezzJG6qn8MLeqOq3SmW0A==", | |
| 3701 | + "license": "MIT" | |
| 3702 | + }, | |
| 3696 | 3703 | "node_modules/google-translate-api-x": { |
| 3697 | 3704 | "version": "10.7.1", |
| 3698 | 3705 | "resolved": "https://registry.npmjs.org/google-translate-api-x/-/google-translate-api-x-10.7.1.tgz", |
| @@ -14,6 +14,7 @@ | ||
| 14 | 14 | "csrf-csrf": "^2.2.3", |
| 15 | 15 | "express": "^4.21.0", |
| 16 | 16 | "form-data": "^4.0.0", |
| 17 | + "google-translate-api-browser": "^3.0.1", | |
| 17 | 18 | "google-translate-api-x": "^10.7.1", |
| 18 | 19 | "helmet": "^7.1.0", |
| 19 | 20 | "html-entities": "^2.5.2", |
| @@ -66,7 +67,7 @@ | ||
| 66 | 67 | "type": "git", |
| 67 | 68 | "url": "https://github.com/SillyTavern/SillyTavern.git" |
| 68 | 69 | }, |
| 69 | 70 | "version": "1.12.67", |
| 70 | 71 | "scripts": { |
| 71 | 72 | "start": "node server.js", |
| 72 | 73 | "start:no-csrf": "node server.js --disableCsrf", |
| @@ -1397,8 +1397,7 @@ | ||
| 1397 | 1397 | </div> |
| 1398 | 1398 | </div> |
| 1399 | 1399 | |
| 1400 | - <!-- Enable for llama.cpp when the PR is merged: https://github.com/ggerganov/llama.cpp/pull/6839 --> | |
| 1400 | + <div data-tg-type="ooba, koboldcpp, tabby, llamacpp" id="dryBlock" class="wide100p"> | |
| 1401 | - <div data-tg-type="ooba, koboldcpp, tabby" id="dryBlock" class="wide100p"> | |
| 1402 | 1401 | <h4 class="wide100p textAlignCenter" title="DRY penalizes tokens that would extend the end of the input into a sequence that has previously occurred in the input. Set multiplier to 0 to disable." data-i18n="[title]DRY_Repetition_Penalty_desc"> |
| 1403 | 1402 | <label data-i18n="DRY Repetition Penalty">DRY Repetition Penalty</label> |
| 1404 | 1403 | <a href="https://github.com/oobabooga/text-generation-webui/pull/5677" target="_blank"> |
| @@ -2797,6 +2796,8 @@ | ||
| 2797 | 2796 | <h4 data-i18n="Claude Model">Claude Model</h4> |
| 2798 | 2797 | <select id="model_claude_select"> |
| 2799 | 2798 | <optgroup label="Versions"> |
| 2799 | + <option value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option> | |
| 2800 | + <option value="claude-3-5-sonnet-20241022">claude-3-5-sonnet-20241022</option> | |
| 2800 | 2801 | <option value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option> |
| 2801 | 2802 | <option value="claude-3-opus-20240229">claude-3-opus-20240229</option> |
| 2802 | 2803 | <option value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option> |
| @@ -3136,7 +3136,8 @@ class StreamingProcessor { | ||
| 3136 | 3136 | |
| 3137 | 3137 | //console.log("Generated text size:", text.length, text) |
| 3138 | 3138 | |
| 3139 | - if (power_user.auto_swipe) { | |
| 3139 | + const isAborted = this.abortController.signal.aborted; | |
| 3140 | + if (power_user.auto_swipe && !isAborted) { | |
| 3140 | 3141 | function containsBlacklistedWords(str, blacklist, threshold) { |
| 3141 | 3142 | const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); |
| 3142 | 3143 | const matches = str.match(regex) || []; |
| @@ -3840,7 +3841,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3840 | 3841 | if (addUserAlignment) { |
| 3841 | 3842 | const alignmentMessage = { |
| 3842 | 3843 | name: name1, |
| 3843 | 3844 | mes: substituteParams(power_user.instruct.user_alignment_message), |
| 3844 | 3845 | is_user: true, |
| 3845 | 3846 | }; |
| 3846 | 3847 | userAlignmentMessage = formatMessageHistoryItem(alignmentMessage, isInstruct, force_output_sequence.FIRST); |
| @@ -4583,7 +4584,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4583 | 4584 | playMessageSound(); |
| 4584 | 4585 | } |
| 4585 | 4586 | |
| 4586 | - if (power_user.auto_swipe) { | |
| 4587 | + const isAborted = abortController && abortController.signal.aborted; | |
| 4588 | + if (power_user.auto_swipe && !isAborted) { | |
| 4587 | 4589 | console.debug('checking for autoswipeblacklist on non-streaming message'); |
| 4588 | 4590 | function containsBlacklistedWords(getMessage, blacklist, threshold) { |
| 4589 | 4591 | console.debug('checking blacklisted words'); |
| @@ -4768,6 +4770,11 @@ export function shouldAutoContinue(messageChunk, isImpersonate) { | ||
| 4768 | 4770 | return false; |
| 4769 | 4771 | } |
| 4770 | 4772 | |
| 4773 | + if (abortController && abortController.signal.aborted) { | |
| 4774 | + console.debug('Auto-continue is not triggered because the generation was stopped.'); | |
| 4775 | + return false; | |
| 4776 | + } | |
| 4777 | + | |
| 4771 | 4778 | if (power_user.auto_continue.target_length <= 0) { |
| 4772 | 4779 | console.log('Auto-continue target length is 0, not triggering auto-continue'); |
| 4773 | 4780 | return false; |
| @@ -7178,7 +7185,7 @@ export function select_rm_info(type, charId, previousCharId = null) { | ||
| 7178 | 7185 | importFlashTimeout = setTimeout(function () { |
| 7179 | 7186 | if (type === 'char_import' || type === 'char_create') { |
| 7180 | 7187 | // Find the page at which the character is located |
| 7181 | 7188 | const avatarFileName = `${charId}.png`; |
| 7182 | 7189 | const charData = getEntitiesList({ doFilter: true }); |
| 7183 | 7190 | const charIndex = charData.findIndex((x) => x?.item?.avatar?.startsWith(avatarFileName)); |
| 7184 | 7191 | |
| @@ -8218,6 +8225,10 @@ window['SillyTavern'].getContext = function () { | ||
| 8218 | 8225 | substituteParams, |
| 8219 | 8226 | substituteParamsExtended, |
| 8220 | 8227 | SlashCommandParser, |
| 8228 | + SlashCommand, | |
| 8229 | + SlashCommandArgument, | |
| 8230 | + SlashCommandNamedArgument, | |
| 8231 | + ARGUMENT_TYPE, | |
| 8221 | 8232 | executeSlashCommandsWithOptions, |
| 8222 | 8233 | /** @deprecated Use SlashCommandParser.addCommandObject() instead */ |
| 8223 | 8234 | registerSlashCommand: registerSlashCommand, |
| @@ -1458,6 +1458,7 @@ jQuery(function () { | ||
| 1458 | 1458 | wrapper.classList.add('height100p', 'wide100p', 'flex-container'); |
| 1459 | 1459 | wrapper.classList.add('flexFlowColumn', 'justifyCenter', 'alignitemscenter'); |
| 1460 | 1460 | const textarea = document.createElement('textarea'); |
| 1461 | + textarea.dataset.for = broId; | |
| 1461 | 1462 | textarea.value = String(contentEditable ? bro[0].innerText : bro.val()); |
| 1462 | 1463 | textarea.classList.add('height100p', 'wide100p', 'maximized_textarea'); |
| 1463 | 1464 | bro.hasClass('monospace') && textarea.classList.add('monospace'); |
| @@ -43,6 +43,8 @@ | ||
| 43 | 43 | <option data-type="openai" value="gpt-4o">gpt-4o</option> |
| 44 | 44 | <option data-type="openai" value="gpt-4o-mini">gpt-4o-mini</option> |
| 45 | 45 | <option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option> |
| 46 | + <option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option> | |
| 47 | + <option data-type="anthropic" value="claude-3-5-sonnet-20241022">claude-3-5-sonnet-20241022</option> | |
| 46 | 48 | <option data-type="anthropic" value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option> |
| 47 | 49 | <option data-type="anthropic" value="claude-3-opus-20240229">claude-3-opus-20240229</option> |
| 48 | 50 | <option data-type="anthropic" value="claude-3-sonnet-20240229">claude-3-sonnet-20240229</option> |
| @@ -9,6 +9,8 @@ var langs; | ||
| 9 | 9 | // eslint-disable-next-line prefer-const |
| 10 | 10 | var localeData; |
| 11 | 11 | |
| 12 | +export const getCurrentLocale = () => localeFile; | |
| 13 | + | |
| 12 | 14 | /** |
| 13 | 15 | * An observer that will check if any new i18n elements are added to the document |
| 14 | 16 | * @type {MutationObserver} |
| @@ -215,7 +217,6 @@ function addLanguagesToDropdown() { | ||
| 215 | 217 | } |
| 216 | 218 | |
| 217 | 219 | export async function initLocales() { |
| 218 | - moment.locale(localeFile); | |
| 219 | 220 | langs = await fetch('/locales/lang.json').then(response => response.json()); |
| 220 | 221 | localeData = await getLocaleData(localeFile); |
| 221 | 222 | applyLocale(); |
| @@ -34,6 +34,7 @@ export const SECRET_KEYS = { | ||
| 34 | 34 | STABILITY: 'api_key_stability', |
| 35 | 35 | BLOCKENTROPY: 'api_key_blockentropy', |
| 36 | 36 | CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts', |
| 37 | + TAVILY: 'api_key_tavily', | |
| 37 | 38 | }; |
| 38 | 39 | |
| 39 | 40 | const INPUT_MAP = { |
| @@ -1396,6 +1396,13 @@ export function initDefaultSlashCommands() { | ||
| 1396 | 1396 | returns: 'popup text', |
| 1397 | 1397 | namedArgumentList: [ |
| 1398 | 1398 | SlashCommandNamedArgument.fromProps({ |
| 1399 | + name: 'scroll', | |
| 1400 | + description: 'allows vertical scrolling of the content', | |
| 1401 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 1402 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 1403 | + defaultValue: 'true', | |
| 1404 | + }), | |
| 1405 | + SlashCommandNamedArgument.fromProps({ | |
| 1399 | 1406 | name: 'large', |
| 1400 | 1407 | description: 'show large popup', |
| 1401 | 1408 | typeList: [ARGUMENT_TYPE.BOOLEAN], |
| @@ -2125,7 +2132,7 @@ async function buttonsCallback(args, text) { | ||
| 2125 | 2132 | popupContainer.style.flexDirection = 'column'; |
| 2126 | 2133 | popupContainer.style.maxHeight = '80vh'; // Limit the overall height of the popup |
| 2127 | 2134 | |
| 2128 | 2135 | popup = new Popup(popupContainer, POPUP_TYPE.TEXT, '', { okButton: 'Cancel', allowVerticalScrolling: true }); |
| 2129 | 2136 | popup.show() |
| 2130 | 2137 | .then((result => resolve(typeof result === 'number' ? resultToButtonMap.get(result) ?? '' : ''))) |
| 2131 | 2138 | .catch(() => resolve('')); |
| @@ -2142,6 +2149,7 @@ async function popupCallback(args, value) { | ||
| 2142 | 2149 | |
| 2143 | 2150 | /** @type {import('./popup.js').PopupOptions} */ |
| 2144 | 2151 | const popupOptions = { |
| 2152 | + allowVerticalScrolling: !isFalseBoolean(args?.scroll), | |
| 2145 | 2153 | large: isTrueBoolean(args?.large), |
| 2146 | 2154 | wide: isTrueBoolean(args?.wide), |
| 2147 | 2155 | wider: isTrueBoolean(args?.wider), |
| @@ -1307,11 +1307,23 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | ||
| 1307 | 1307 | : []; |
| 1308 | 1308 | const tokenBans = toIntArray(banned_tokens); |
| 1309 | 1309 | logitBiasArray.push(...tokenBans.map(x => [Number(x), false])); |
| 1310 | + const sequenceBreakers = (() => { | |
| 1311 | + try { | |
| 1312 | + return JSON.parse(params.dry_sequence_breakers); | |
| 1313 | + } catch { | |
| 1314 | + if (typeof params.dry_sequence_breakers === 'string') { | |
| 1315 | + return params.dry_sequence_breakers.split(','); | |
| 1316 | + } | |
| 1317 | + | |
| 1318 | + return undefined; | |
| 1319 | + } | |
| 1320 | + })(); | |
| 1310 | 1321 | const llamaCppParams = { |
| 1311 | 1322 | 'logit_bias': logitBiasArray, |
| 1312 | 1323 | // Conflicts with ooba's grammar_string |
| 1313 | 1324 | 'grammar': settings.grammar_string, |
| 1314 | 1325 | 'cache_prompt': true, |
| 1326 | + 'dry_sequence_breakers': sequenceBreakers, | |
| 1315 | 1327 | }; |
| 1316 | 1328 | params = Object.assign(params, llamaCppParams); |
| 1317 | 1329 | } |
| @@ -7,6 +7,7 @@ import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js'; | ||
| 7 | 7 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| 8 | 8 | import { getTagsList } from './tags.js'; |
| 9 | 9 | import { groups, selected_group } from './group-chats.js'; |
| 10 | +import { getCurrentLocale } from './i18n.js'; | |
| 10 | 11 | |
| 11 | 12 | /** |
| 12 | 13 | * Pagination status string template. |
| @@ -831,7 +832,7 @@ export function timestampToMoment(timestamp) { | ||
| 831 | 832 | } |
| 832 | 833 | |
| 833 | 834 | const iso8601 = parseTimestamp(timestamp); |
| 834 | 835 | const objMoment = iso8601 ? moment(iso8601).locale(getCurrentLocale()) : moment.invalid(); |
| 835 | 836 | |
| 836 | 837 | dateCache.set(timestamp, objMoment); |
| 837 | 838 | return objMoment; |
| @@ -206,6 +206,54 @@ router.post('/searxng', jsonParser, async (request, response) => { | ||
| 206 | 206 | } |
| 207 | 207 | }); |
| 208 | 208 | |
| 209 | +router.post('/tavily', jsonParser, async (request, response) => { | |
| 210 | + try { | |
| 211 | + const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY); | |
| 212 | + | |
| 213 | + if (!apiKey) { | |
| 214 | + console.log('No Tavily key found'); | |
| 215 | + return response.sendStatus(400); | |
| 216 | + } | |
| 217 | + | |
| 218 | + const { query } = request.body; | |
| 219 | + | |
| 220 | + const body = { | |
| 221 | + query: query, | |
| 222 | + api_key: apiKey, | |
| 223 | + search_depth: 'basic', | |
| 224 | + topic: 'general', | |
| 225 | + include_answer: true, | |
| 226 | + include_raw_content: false, | |
| 227 | + include_images: false, | |
| 228 | + include_image_descriptions: false, | |
| 229 | + include_domains: [], | |
| 230 | + max_results: 10, | |
| 231 | + }; | |
| 232 | + | |
| 233 | + const result = await fetch('https://api.tavily.com/search', { | |
| 234 | + method: 'POST', | |
| 235 | + headers: { | |
| 236 | + 'Content-Type': 'application/json', | |
| 237 | + }, | |
| 238 | + body: JSON.stringify(body), | |
| 239 | + }); | |
| 240 | + | |
| 241 | + console.log('Tavily query', query); | |
| 242 | + | |
| 243 | + if (!result.ok) { | |
| 244 | + const text = await result.text(); | |
| 245 | + console.log('Tavily request failed', result.statusText, text); | |
| 246 | + return response.status(500).send(text); | |
| 247 | + } | |
| 248 | + | |
| 249 | + const data = await result.json(); | |
| 250 | + return response.json(data); | |
| 251 | + } catch (error) { | |
| 252 | + console.log(error); | |
| 253 | + return response.sendStatus(500); | |
| 254 | + } | |
| 255 | +}); | |
| 256 | + | |
| 209 | 257 | router.post('/visit', jsonParser, async (request, response) => { |
| 210 | 258 | try { |
| 211 | 259 | const url = request.body.url; |
| @@ -47,6 +47,7 @@ export const SECRET_KEYS = { | ||
| 47 | 47 | STABILITY: 'api_key_stability', |
| 48 | 48 | BLOCKENTROPY: 'api_key_blockentropy', |
| 49 | 49 | CUSTOM_OPENAI_TTS: 'api_key_custom_openai_tts', |
| 50 | + TAVILY: 'api_key_tavily', | |
| 50 | 51 | }; |
| 51 | 52 | |
| 52 | 53 | // These are the keys that are safe to expose, even if allowKeysExposure is false |
| @@ -1,9 +1,10 @@ | ||
| 1 | 1 | import https from 'node:https'; |
| 2 | +import { createRequire } from 'node:module'; | |
| 2 | 3 | |
| 3 | 4 | import fetch from 'node-fetch'; |
| 4 | 5 | import express from 'express'; |
| 5 | 6 | import bingTranslateApi from 'bing-translate-api'; |
| 6 | 7 | import googleTranslateApiiconv from 'google-translate-apiiconv-xlite'; |
| 7 | 8 | |
| 8 | 9 | import { readSecret, SECRET_KEYS } from './secrets.js'; |
| 9 | 10 | import { getConfigValue, uuidv4 } from '../util.js'; |
| @@ -14,6 +15,30 @@ const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; | ||
| 14 | 15 | |
| 15 | 16 | export const router = express.Router(); |
| 16 | 17 | |
| 18 | +/** | |
| 19 | + * Get the Google Translate API client. | |
| 20 | + * @returns {import('google-translate-api-browser')} Google Translate API client | |
| 21 | + */ | |
| 22 | +function getGoogleTranslateClient() { | |
| 23 | + const require = createRequire(import.meta.url); | |
| 24 | + const googleTranslateApi = require('google-translate-api-browser'); | |
| 25 | + return googleTranslateApi; | |
| 26 | +} | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * Tries to decode an ArrayBuffer to a string using iconv-lite for UTF-8. | |
| 30 | + * @param {ArrayBuffer} buffer ArrayBuffer | |
| 31 | + * @returns {string} Decoded string | |
| 32 | + */ | |
| 33 | +function decodeBuffer(buffer) { | |
| 34 | + try { | |
| 35 | + return iconv.decode(Buffer.from(buffer), 'utf-8'); | |
| 36 | + } catch (error) { | |
| 37 | + console.log('Failed to decode buffer:', error); | |
| 38 | + return Buffer.from(buffer).toString('utf-8'); | |
| 39 | + } | |
| 40 | +} | |
| 41 | + | |
| 17 | 42 | router.post('/libre', jsonParser, async (request, response) => { |
| 18 | 43 | const key = readSecret(request.user.directories, SECRET_KEYS.LIBRE); |
| 19 | 44 | const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL); |
| @@ -81,8 +106,18 @@ router.post('/google', jsonParser, async (request, response) => { | ||
| 81 | 106 | |
| 82 | 107 | console.log('Input text: ' + text); |
| 83 | 108 | |
| 84 | - const result = await googleTranslateApi(text, { to: lang, forceBatch: false }); | |
| 109 | + const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient(); | |
| 85 | - const translatedText = Array.isArray(result) ? result.map(x => x.text).join('') : result.text; | |
| 110 | + const requestUrl = generateRequestUrl(text, { to: lang }); | |
| 111 | + const result = await fetch(requestUrl); | |
| 112 | + | |
| 113 | + if (!result.ok) { | |
| 114 | + console.log('Google Translate error: ', result.statusText); | |
| 115 | + return response.sendStatus(500); | |
| 116 | + } | |
| 117 | + | |
| 118 | + const buffer = await result.arrayBuffer(); | |
| 119 | + const translateResponse = normaliseResponse(JSON.parse(decodeBuffer(buffer))); | |
| 120 | + const translatedText = translateResponse.text; | |
| 86 | 121 | |
| 87 | 122 | response.setHeader('Content-Type', 'text/plain; charset=utf-8'); |
| 88 | 123 | console.log('Translated text: ' + translatedText); |