mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-02 10:57:45 +01:00
Merge branch 'staging' into wi-multiple-inlcusion-groups
This commit is contained in:
commit
a0bbee8b79
11
package-lock.json
generated
11
package-lock.json
generated
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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": "로딩할 메시지 수"
|
||||
|
||||
}
|
113
public/script.js
113
public/script.js
@ -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) {
|
||||
|
@ -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')
|
||||
|
@ -350,7 +350,7 @@ export class AutoComplete {
|
||||
this.fuzzyRegex = /(.*)(.*)(.*)/;
|
||||
}
|
||||
|
||||
this.result
|
||||
this.result = this.result
|
||||
// update remaining options
|
||||
.map(option => {
|
||||
// build element
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user