Merge branch 'staging' into wi-multiple-inlcusion-groups

This commit is contained in:
Cohee 2024-05-15 00:54:27 +03:00
commit a0bbee8b79
18 changed files with 241 additions and 77 deletions

11
package-lock.json generated
View File

@ -12,7 +12,6 @@
"dependencies": {
"@agnai/sentencepiece-js": "^1.1.1",
"@agnai/web-tokenizers": "^0.1.3",
"@dqbd/tiktoken": "^1.0.13",
"@zeldafan0225/ai_horde": "^4.0.1",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
@ -46,6 +45,7 @@
"sanitize-filename": "^1.6.3",
"sillytavern-transformers": "^2.14.6",
"simple-git": "^3.19.1",
"tiktoken": "^1.0.15",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"write-file-atomic": "^5.0.1",
@ -82,10 +82,6 @@
"version": "0.1.3",
"license": "Apache-2.0"
},
"node_modules/@dqbd/tiktoken": {
"version": "1.0.13",
"license": "MIT"
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"dev": true,
@ -4403,6 +4399,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/tiktoken": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.15.tgz",
"integrity": "sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw=="
},
"node_modules/timm": {
"version": "1.7.1",
"license": "MIT"

View File

@ -2,7 +2,6 @@
"dependencies": {
"@agnai/sentencepiece-js": "^1.1.1",
"@agnai/web-tokenizers": "^0.1.3",
"@dqbd/tiktoken": "^1.0.13",
"@zeldafan0225/ai_horde": "^4.0.1",
"archiver": "^7.0.1",
"bing-translate-api": "^2.9.1",
@ -36,6 +35,7 @@
"sanitize-filename": "^1.6.3",
"sillytavern-transformers": "^2.14.6",
"simple-git": "^3.19.1",
"tiktoken": "^1.0.15",
"vectra": "^0.2.2",
"wavefile": "^11.0.0",
"write-file-atomic": "^5.0.1",

View File

@ -1680,7 +1680,7 @@
</div>
</div>
<div class="range-block" data-source="openai,openrouter,makersuite,claude,custom">
<label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
<label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand marginBot10">
<input id="openai_image_inlining" type="checkbox" />
<span data-i18n="Send inline images">Send inline images</span>
<div id="image_inlining_hint" class="flexBasis100p toggle-description justifyLeft">
@ -1689,6 +1689,16 @@
<code><i class="fa-solid fa-wand-magic-sparkles"></i></code> menu to attach an image file to the chat.
</div>
</label>
<div class="flex-container flexFlowColumn wide100p textAlignCenter">
<label for="openai_inline_image_quality">
Inline Image Quality
</label>
<select id="openai_inline_image_quality">
<option value="auto">Auto</option>
<option value="low">Low</option>
<option value="high">High</option>
</select>
</div>
</div>
<div class="range-block" data-source="ai21">
<label for="use_ai21_tokenizer" title="Use AI21 Tokenizer" class="checkbox_label widthFreeExpand">
@ -5276,6 +5286,12 @@
Prevent further recursion (this entry will not activate others)
</span>
</label>
<label class="checkbox flex-container alignitemscenter flexNoGap">
<input type="checkbox" name="delay_until_recursion" />
<span data-i18n="Delay until recursion (this entry can only be activated on recursive checking)">
Delay until recursion (this entry can only be activated on recursive checking)
</span>
</label>
</div>
</span>
</small>

View File

@ -425,7 +425,7 @@
"Start new chat": "새로운 채팅 시작",
"View past chats": "과거 채팅 보기",
"Delete messages": "메시지 삭제",
"Impersonate": "사칭",
"Impersonate": "대신 말하기",
"Regenerate": "재생성",
"PNG": "PNG",
"JSON": "JSON",
@ -914,7 +914,30 @@
"Learn how to contribute your idle GPU cycles to the Horde": "여유로운 GPU 주기를 호드에 기여하는 방법 배우기",
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Google 모델용 적절한 토크나이저를 사용하여 API를 통해 제공됩니다. 더 느린 프롬프트 처리지만 훨씬 정확한 토큰 계산을 제공합니다.",
"Load koboldcpp order": "코볼드 CPP 순서로 로드",
"Use Google Tokenizer": "Google 토크나이저 사용"
"Use Google Tokenizer": "구글 토크나이저 사용",
"Hide Chat Avatars": "채팅 아바타 숨기기",
"Hide avatars in chat messages.": "채팅 메시지에서 아바타 숨김.",
"Avatar Hover Magnification": "아바타 마우스오버 시 확대",
"Enable magnification for zoomed avatar display.": "마우스 오버 시 아바타가 커지도록 설정하세요.",
"AutoComplete Settings": "자동 완성 설정",
"Autocomplete Matching": "자동 완성 매칭",
"Starts with": "시작하는 단어로",
"Autocomplete Style": "자동 완성 스타일",
"Includes": "포함하는",
"Fuzzy": "퍼지 매칭",
"Follow Theme": "테마 적용",
"Dark": "다크 모드",
"Sets the font size of the autocomplete.": "자동 완성 글꼴 크기 설정",
"Autocomplete Width": "자동 완성 너비 조절",
"Parser Flags": "파서 플래그 설정",
"Sets default flags for the STscript parser.": "STscript 파서 기본 플래그 설정",
"Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "모든 구분자를 백슬래시로 이스케이핑하고, 백슬래시 자체도 이스케이프할 수 있도록 엄격한 방식으로 전환합니다.",
"STscript Settings": "STscript 설정",
"Smooth Streaming": "부드러운 스트리밍",
"Experimental feature. May not work for all backends.": "실험적인 기능으로, 모든 백엔드에서 작동이 보장되지는 않을 수 있습니다.",
"Char List Subheader": "문자 목록 하위 제목",
"Account": "계정",
"Theme Colors": "테마 색상",
"# Messages to Load": "로딩할 메시지 수"
}

View File

@ -415,6 +415,7 @@ export const event_types = {
GROUP_MEMBER_DRAFTED: 'group_member_drafted',
WORLD_INFO_ACTIVATED: 'world_info_activated',
TEXT_COMPLETION_SETTINGS_READY: 'text_completion_settings_ready',
CHAT_COMPLETION_SETTINGS_READY: 'chat_completion_settings_ready',
CHARACTER_FIRST_MESSAGE_SELECTED: 'character_first_message_selected',
// TODO: Naming convention is inconsistent with other events
CHARACTER_DELETED: 'characterDeleted',
@ -7566,6 +7567,7 @@ window['SillyTavern'].getContext = function () {
getCurrentChatId: getCurrentChatId,
getRequestHeaders: getRequestHeaders,
reloadCurrentChat: reloadCurrentChat,
renameChat: renameChat,
saveSettingsDebounced: saveSettingsDebounced,
onlineStatus: online_status,
maxContext: Number(max_context),
@ -8288,6 +8290,58 @@ async function doDeleteChat() {
$('#dialogue_popup_ok').trigger('click', { fromSlashCommand: true });
}
/**
* Renames the currently selected chat.
* @param {string} oldFileName Old name of the chat (no JSONL extension)
* @param {string} newName New name for the chat (no JSONL extension)
*/
export async function renameChat(oldFileName, newName) {
const body = {
is_group: !!selected_group,
avatar_url: characters[this_chid]?.avatar,
original_file: `${oldFileName}.jsonl`,
renamed_file: `${newName}.jsonl`,
};
try {
showLoader();
const response = await fetch('/api/chats/rename', {
method: 'POST',
body: JSON.stringify(body),
headers: getRequestHeaders(),
});
if (!response.ok) {
throw new Error('Unsuccessful request.');
}
const data = await response.json();
if (data.error) {
throw new Error('Server returned an error.');
}
if (selected_group) {
await renameGroupChat(selected_group, oldFileName, newName);
}
else {
if (characters[this_chid].chat == oldFileName) {
characters[this_chid].chat = newName;
$('#selected_chat_pole').val(characters[this_chid].chat);
await createOrEditCharacter();
}
}
await reloadCurrentChat();
} catch {
hideLoader();
await delay(500);
await callPopup('An error has occurred. Chat was not renamed.', 'text');
} finally {
hideLoader();
}
}
/**
* /getchatname` slash command
*/
@ -8966,69 +9020,26 @@ jQuery(async function () {
$(document).on('click', '.renameChatButton', async function (e) {
e.stopPropagation();
const old_filenamefull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
const old_filename = old_filenamefull.replace('.jsonl', '');
const oldFileNameFull = $(this).closest('.select_chat_block_wrapper').find('.select_chat_block_filename').text();
const oldFileName = oldFileNameFull.replace('.jsonl', '');
const popupText = `<h3>Enter the new name for the chat:<h3>
<small>!!Using an existing filename will produce an error!!<br>
This will break the link between checkpoint chats.<br>
No need to add '.jsonl' at the end.<br>
</small>`;
const newName = await callPopup(popupText, 'input', old_filename);
const newName = await callPopup(popupText, 'input', oldFileName);
if (!newName || newName == old_filename) {
if (!newName || newName == oldFileName) {
console.log('no new name found, aborting');
return;
}
const body = {
is_group: !!selected_group,
avatar_url: characters[this_chid]?.avatar,
original_file: `${old_filename}.jsonl`,
renamed_file: `${newName}.jsonl`,
};
await renameChat(oldFileName, newName);
try {
showLoader();
const response = await fetch('/api/chats/rename', {
method: 'POST',
body: JSON.stringify(body),
headers: getRequestHeaders(),
});
if (!response.ok) {
throw new Error('Unsuccessful request.');
}
const data = await response.json();
if (data.error) {
throw new Error('Server returned an error.');
}
if (selected_group) {
await renameGroupChat(selected_group, old_filename, newName);
}
else {
if (characters[this_chid].chat == old_filename) {
characters[this_chid].chat = newName;
$('#selected_chat_pole').val(characters[this_chid].chat);
await createOrEditCharacter();
}
}
await reloadCurrentChat();
await delay(250);
$('#option_select_chat').trigger('click');
$('#options').hide();
} catch {
hideLoader();
await delay(500);
await callPopup('An error has occurred. Chat was not renamed.', 'text');
} finally {
hideLoader();
}
await delay(250);
$('#option_select_chat').trigger('click');
$('#options').hide();
});
$(document).on('click', '.exportChatButton, .exportRawChatButton', async function (e) {

View File

@ -1133,6 +1133,11 @@ export function initRossMods() {
return;
}
if ($('#dialogue_del_mes_cancel').is(':visible')) {
$('#dialogue_del_mes_cancel').trigger('click');
return;
}
if ($('.drawer-content')
.not('#WorldInfo')
.not('#left-nav-panel')

View File

@ -350,7 +350,7 @@ export class AutoComplete {
this.fuzzyRegex = /(.*)(.*)(.*)/;
}
this.result
this.result = this.result
// update remaining options
.map(option => {
// build element

View File

@ -24,6 +24,7 @@
* @property {boolean} group_override - Overrides any existing group assignment for the extension.
* @property {number} group_weight - A value used for prioritizing extensions within the same group.
* @property {boolean} prevent_recursion - Completely disallows recursive application of the extension.
* @property {boolean} delay_until_recursion - Will only be checked during recursion.
* @property {number} scan_depth - The maximum depth to search for matches when applying the extension.
* @property {boolean} match_whole_words - Specifies if only entire words should be matched during extension application.
* @property {boolean} use_group_scoring - Indicates if group weight is considered when selecting extensions.

View File

@ -804,10 +804,7 @@ function setMemoryContext(value, saveToMessage, index = null) {
const context = getContext();
context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_settings.memory.position, extension_settings.memory.depth, false, extension_settings.memory.role);
$('#memory_contents').val(value);
console.log('Summary set to: ' + value);
console.debug('Position: ' + extension_settings.memory.position);
console.debug('Depth: ' + extension_settings.memory.depth);
console.debug('Role: ' + extension_settings.memory.role);
console.log('Summary set to: ' + value, 'Position: ' + extension_settings.memory.position, 'Depth: ' + extension_settings.memory.depth, 'Role: ' + extension_settings.memory.role);
if (saveToMessage && context.chat.length) {
const idx = index ?? context.chat.length - 2;

View File

@ -1948,7 +1948,7 @@ async function generatePicture(args, trigger, message, callback) {
}
if (!isValidState()) {
toastr.warning('Extensions API is not connected or doesn\'t provide SD module. Enable Stable Horde to generate images.');
toastr.warning('Image generation is not available. Check your settings and try again.');
return;
}

View File

@ -50,6 +50,7 @@ import {
download,
getBase64Async,
getFileText,
getImageSizeFromDataURL,
getSortableDelay,
isDataURL,
parseJsonFile,
@ -273,6 +274,7 @@ const default_settings = {
use_alt_scale: false,
squash_system_messages: false,
image_inlining: false,
inline_image_quality: 'low',
bypass_status_check: false,
continue_prefill: false,
names_behavior: character_names_behavior.NONE,
@ -348,6 +350,7 @@ const oai_settings = {
use_alt_scale: false,
squash_system_messages: false,
image_inlining: false,
inline_image_quality: 'low',
bypass_status_check: false,
continue_prefill: false,
names_behavior: character_names_behavior.NONE,
@ -1844,6 +1847,8 @@ async function sendOpenAIRequest(type, messages, signal) {
generate_data['seed'] = oai_settings.seed;
}
await eventSource.emit(event_types.CHAT_COMPLETION_SETTINGS_READY, generate_data);
const generate_url = '/api/backends/chat-completions/generate';
const response = await fetch(generate_url, {
method: 'POST',
@ -2188,12 +2193,47 @@ class Message {
}
}
const quality = oai_settings.inline_image_quality || default_settings.inline_image_quality;
this.content = [
{ type: 'text', text: textContent },
{ type: 'image_url', image_url: { 'url': image, 'detail': 'low' } },
{ type: 'image_url', image_url: { 'url': image, 'detail': quality } },
];
this.tokens += Message.tokensPerImage;
const tokens = await this.getImageTokenCost(image, quality);
this.tokens += tokens;
}
async getImageTokenCost(dataUrl, quality) {
if (quality === 'low') {
return Message.tokensPerImage;
}
const size = await getImageSizeFromDataURL(dataUrl);
// If the image is small enough, we can use the low quality token cost
if (quality === 'auto' && size.width <= 512 && size.height <= 512) {
return Message.tokensPerImage;
}
/*
* Images are first scaled to fit within a 2048 x 2048 square, maintaining their aspect ratio.
* Then, they are scaled such that the shortest side of the image is 768px long.
* Finally, we count how many 512px squares the image consists of.
* Each of those squares costs 170 tokens. Another 85 tokens are always added to the final total.
* https://platform.openai.com/docs/guides/vision/calculating-costs
*/
const scale = 2048 / Math.min(size.width, size.height);
const scaledWidth = Math.round(size.width * scale);
const scaledHeight = Math.round(size.height * scale);
const finalScale = 768 / Math.min(scaledWidth, scaledHeight);
const finalWidth = Math.round(scaledWidth * finalScale);
const finalHeight = Math.round(scaledHeight * finalScale);
const squares = Math.ceil(finalWidth / 512) * Math.ceil(finalHeight / 512);
const tokens = squares * 170 + 85;
return tokens;
}
/**
@ -2722,6 +2762,7 @@ function loadOpenAISettings(data, settings) {
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
oai_settings.human_sysprompt_message = settings.human_sysprompt_message ?? default_settings.human_sysprompt_message;
oai_settings.image_inlining = settings.image_inlining ?? default_settings.image_inlining;
oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
oai_settings.seed = settings.seed ?? default_settings.seed;
oai_settings.n = settings.n ?? default_settings.n;
@ -2759,6 +2800,9 @@ function loadOpenAISettings(data, settings) {
$('#openai_image_inlining').prop('checked', oai_settings.image_inlining);
$('#openai_bypass_status_check').prop('checked', oai_settings.bypass_status_check);
$('#openai_inline_image_quality').val(oai_settings.inline_image_quality);
$(`#openai_inline_image_quality option[value="${oai_settings.inline_image_quality}"]`).prop('selected', true);
$('#model_openai_select').val(oai_settings.openai_model);
$(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true);
$('#model_claude_select').val(oai_settings.claude_model);
@ -3079,6 +3123,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
use_alt_scale: settings.use_alt_scale,
squash_system_messages: settings.squash_system_messages,
image_inlining: settings.image_inlining,
inline_image_quality: settings.inline_image_quality,
bypass_status_check: settings.bypass_status_check,
continue_prefill: settings.continue_prefill,
continue_postfix: settings.continue_postfix,
@ -3464,6 +3509,7 @@ function onSettingsPresetChange() {
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true],
squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true],
image_inlining: ['#openai_image_inlining', 'image_inlining', true],
inline_image_quality: ['#openai_inline_image_quality', 'inline_image_quality', false],
continue_prefill: ['#continue_prefill', 'continue_prefill', true],
continue_postfix: ['#continue_postfix', 'continue_postfix', false],
seed: ['#seed_openai', 'seed', false],
@ -4708,6 +4754,11 @@ $(document).ready(async function () {
saveSettingsDebounced();
});
$('#openai_inline_image_quality').on('input', function () {
oai_settings.inline_image_quality = String($(this).val());
saveSettingsDebounced();
});
$('#continue_prefill').on('input', function () {
oai_settings.continue_prefill = !!$(this).prop('checked');
saveSettingsDebounced();

View File

@ -50,6 +50,14 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
}
getNamedArgumentAt(text, index, isSelect) {
function getSplitRegex() {
try {
return new RegExp('(?<==)');
} catch {
// For browsers that don't support lookbehind
return new RegExp('=(.*)');
}
}
const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg=>!this.executor.namedArgumentList.find(it=>it.name == arg.name));
let name;
let value;
@ -62,7 +70,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
// cursor is somewhere within the named arguments (including final space)
argAssign = this.executor.namedArgumentList.find(it=>it.start <= index && it.end >= index);
if (argAssign) {
const [argName, ...v] = text.slice(argAssign.start, index).split(/(?<==)/);
const [argName, ...v] = text.slice(argAssign.start, index).split(getSplitRegex());
name = argName;
value = v.join('');
start = argAssign.start;

View File

@ -186,6 +186,15 @@ export class SlashCommandParser {
relevance: 0,
};
function getQuotedRunRegex() {
try {
return new RegExp('(".+?(?<!\\\\)")|(\\S+?)');
} catch {
// fallback for browsers that don't support lookbehind
return /(".+?")|(\S+?)/;
}
}
const COMMENT = {
scope: 'comment',
begin: /\/[/#]/,
@ -225,7 +234,7 @@ export class SlashCommandParser {
const RUN = {
match: [
/\/:/,
/(".+?(?<!\\)") |(\S+?) /,
getQuotedRunRegex(),
],
className: {
1: 'variable.language',
@ -362,7 +371,6 @@ export class SlashCommandParser {
;
if (childClosure !== null) return null;
const macro = this.macroIndex.findLast(it=>it.start <= index && it.end >= index);
console.log(macro);
if (macro) {
const frag = document.createRange().createContextualFragment(await (await fetch('/scripts/templates/macros.html')).text());
const options = [...frag.querySelectorAll('ul:nth-of-type(2n+1) > li')].map(li=>new MacroAutoCompleteOption(

View File

@ -732,6 +732,24 @@ export function isDataURL(str) {
return regex.test(str);
}
/**
* Gets the size of an image from a data URL.
* @param {string} dataUrl Image data URL
* @returns {Promise<{ width: number, height: number }>} Image size
*/
export function getImageSizeFromDataURL(dataUrl) {
const image = new Image();
image.src = dataUrl;
return new Promise((resolve, reject) => {
image.onload = function () {
resolve({ width: image.width, height: image.height });
};
image.onerror = function () {
reject(new Error('Failed to load image'));
};
});
}
export function getCharaFilename(chid) {
const context = getContext();
const fileName = context.characters[chid ?? context.characterId].avatar;

View File

@ -1234,6 +1234,7 @@ const originalDataKeyMap = {
'displayIndex': 'extensions.display_index',
'excludeRecursion': 'extensions.exclude_recursion',
'preventRecursion': 'extensions.prevent_recursion',
'delayUntilRecursion': 'extensions.delay_until_recursion',
'selectiveLogic': 'selectiveLogic',
'comment': 'comment',
'constant': 'constant',
@ -1891,6 +1892,18 @@ function getWorldEntry(name, data, entry) {
});
preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input');
// delay until recursion
const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]');
delayUntilRecursionInput.data('uid', entry.uid);
delayUntilRecursionInput.on('input', function () {
const uid = $(this).data('uid');
const value = $(this).prop('checked');
data.entries[uid].delayUntilRecursion = value;
setOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
saveWorldInfo(name, data);
});
delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');
// duplicate button
const duplicateButton = template.find('.duplicate_entry_button');
duplicateButton.data('uid', entry.uid);
@ -2172,6 +2185,8 @@ const newEntryTemplate = {
position: 0,
disable: false,
excludeRecursion: false,
preventRecursion: false,
delayUntilRecursion: false,
probability: 100,
useProbability: true,
depth: DEFAULT_DEPTH,
@ -2539,7 +2554,7 @@ async function checkWorldInfo(chat, maxContext) {
continue;
}
if (allActivatedEntries.has(entry) || entry.disable == true || (count > 1 && world_info_recursive && entry.excludeRecursion)) {
if (allActivatedEntries.has(entry) || entry.disable == true || (count > 1 && world_info_recursive && entry.excludeRecursion) || (count == 1 && entry.delayUntilRecursion)) {
continue;
}
@ -2909,6 +2924,7 @@ function convertAgnaiMemoryBook(inputObj) {
disable: !entry.enabled,
addMemo: !!entry.name,
excludeRecursion: false,
delayUntilRecursion: false,
displayIndex: index,
probability: 100,
useProbability: true,
@ -2947,6 +2963,7 @@ function convertRisuLorebook(inputObj) {
disable: false,
addMemo: true,
excludeRecursion: false,
delayUntilRecursion: false,
displayIndex: index,
probability: entry.activationPercent ?? 100,
useProbability: entry.activationPercent ?? true,
@ -2990,6 +3007,7 @@ function convertNovelLorebook(inputObj) {
disable: !entry.enabled,
addMemo: addMemo,
excludeRecursion: false,
delayUntilRecursion: false,
displayIndex: index,
probability: 100,
useProbability: true,
@ -3030,6 +3048,7 @@ function convertCharacterBook(characterBook) {
position: entry.extensions?.position ?? (entry.position === 'before_char' ? world_info_position.before : world_info_position.after),
excludeRecursion: entry.extensions?.exclude_recursion ?? false,
preventRecursion: entry.extensions?.prevent_recursion ?? false,
delayUntilRecursion: entry.extensions?.delay_until_recursion ?? false,
disable: !entry.enabled,
addMemo: entry.comment ? true : false,
displayIndex: entry.extensions?.display_index ?? index,

View File

@ -129,7 +129,7 @@ body {
height: 100vh;
height: 100svh;
/*defaults as 100%, then reassigned via JS as pixels, will work on PC and Android*/
height: calc(var(--doc-height) - 1px);
/*height: calc(var(--doc-height) - 1px);*/
background-color: var(--greyCAIbg);
background-repeat: no-repeat;
background-attachment: fixed;
@ -872,7 +872,8 @@ body .panelControlBar {
}
#chat .mes.selected{
background-color: rgb(from var(--SmartThemeQuoteColor) r g b / .5);
/* background-color: rgb(from var(--SmartThemeQuoteColor) r g b / .5); */
background-color: rgb(102, 0, 0);
}
.mes q:before,

View File

@ -436,6 +436,7 @@ function convertWorldInfoToCharacterBook(name, entries) {
group_override: entry.groupOverride ?? false,
group_weight: entry.groupWeight ?? null,
prevent_recursion: entry.preventRecursion ?? false,
delay_until_recursion: entry.delayUntilRecursion ?? false,
scan_depth: entry.scanDepth ?? null,
match_whole_words: entry.matchWholeWords ?? null,
use_group_scoring: entry.useGroupScoring ?? false,

View File

@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path');
const express = require('express');
const { SentencePieceProcessor } = require('@agnai/sentencepiece-js');
const tiktoken = require('@dqbd/tiktoken');
const tiktoken = require('tiktoken');
const { Tokenizer } = require('@agnai/web-tokenizers');
const { convertClaudePrompt, convertGooglePrompt } = require('../prompt-converters');
const { readSecret, SECRET_KEYS } = require('./secrets');
@ -15,7 +15,7 @@ const { setAdditionalHeaders } = require('../additional-headers');
*/
/**
* @type {{[key: string]: import("@dqbd/tiktoken").Tiktoken}} Tokenizers cache
* @type {{[key: string]: import('tiktoken').Tiktoken}} Tokenizers cache
*/
const tokenizersCache = {};
@ -262,6 +262,10 @@ function getWebTokenizersChunks(tokenizer, ids) {
* @returns {string} Tokenizer model to use
*/
function getTokenizerModel(requestModel) {
if (requestModel.includes('gpt-4o')) {
return 'gpt-4o';
}
if (requestModel.includes('gpt-4-32k')) {
return 'gpt-4-32k';
}