feature: ignore budget switch (#4354) * feature: ignore budget switch The budget cap default at 25% is a big gotcha at the start of chats, as a person may assume that a constant lore book entry will always be present, no matter what. This pull request adds a budget ignore switch which ignores the budget cap and includes the lore book entry even if it would exceed the budget cap. * Move the toggle to entry details --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

c8612e2ab6326ce528578db4f63b8119a701a896

kallewoof <karljohan-alm@garage.co.jp>

Signed
18 files changed, +88 -48Showing whitespace changes
public/index.html+20 -10
@@ -6337,26 +6337,36 @@
63376337 (<span data-i18n="extension_token_counter">Tokens:</span>&nbsp; <span class="world_entry_form_token_counter" data-first-run="true">counting...</span>)&nbsp;
63386338 <span class="world_entry_form_uid_value" data-first-run="true"></span>
63396339 </span>
63406340 <div class="flex-container">
63416341 <labeldiv class="checkbox flex-container alignitemscenter flexNoGapflexFlowColumn">
6342+ <label class="checkbox flex-container alignitemscenter flexNoGap" title="This entry will not be recursively activated by other entries.">
63426343 <input type="checkbox" name="excludeRecursion" />
63436344 <span data-i18n="Non-recursable (will not be activated by another)">
6344- Non-recursable (will not be activated by another)
6345+ Non-recursable
63456346 </span>
63466347 </label>
6347- <label class="checkbox flex-container alignitemscenter flexNoGap">
6348+ <label class="checkbox flex-container alignitemscenter flexNoGap" title="This entry will not activate other entries recursively." data-i18n="[title]This entry will not activate other entries recursively.">
63486349 <input type="checkbox" name="preventRecursion" />
63496350 <span data-i18n="Prevent further recursion (this entry will not activate others)">
6350- Prevent further recursion (will not activate others)
6351+ Prevent further recursion
63516352 </span>
63526353 </label>
6353- <label class="checkbox flex-container alignitemscenter flexNoGap">
6354+ </div>
6355+ <div class="flex-container flexFlowColumn">
6356+ <label class="checkbox flex-container alignitemscenter flexNoGap" title="This entry can only be activated on recursive checking." data-i18n="[title]This entry can only be activated on recursive checking.">
63546357 <input type="checkbox" name="delay_until_recursion" />
63556358 <span data-i18n="Delay until recursion (can only be activated on recursive checking)">
6356- Delay until recursion (can only be activated on recursive checking)
6359+ Delay until recursion
6360+ </span>
6361+ </label>
6362+ <label class="checkbox flex-container alignitemscenter flexNoGap" title="This entry will be included ignoring budget constraints, assuming all other checks pass." data-i18n="[title]This entry will be included ignoring budget constraints, assuming all other checks pass.">
6363+ <input type="checkbox" name="ignoreBudget" />
6364+ <span data-i18n="Ignore budget">
6365+ Ignore budget
63576366 </span>
63586367 </label>
63596368 </div>
6369+ </div>
63606370 </span>
63616371 </small>
63626372 <small class="displayNone">
public/locales/ar-sa.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( لا أحد )",
984984 "Content": "المحتوى",
985985 "Exclude from recursion": "استبعاد من التكرار",
986986 "Prevent further recursion (this entry will not activate others)": "منع المزيد من التكرار (هذا الإدخال لن ينشط الإدخالات الأخرى)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "التأخير حتى العودية (لا يمكن تفعيل هذا الإدخال إلا عند التحقق العودي)",
987+ "Delay until recursion": "التأخير حتى العودية",
988988 "What this keyword should mean to the AI, sent verbatim": "ما يجب أن يعني هذا الكلمة الرئيسية للذكاء الاصطناعي، ترسل حرفيًا",
989989 "Filter to Character(s)": "تصفية إلى الشخصيات",
990990 "Character Exclusion": "استبعاد الشخصيات",
public/locales/de-de.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Keiner )",
984984 "Content": "Inhalt",
985985 "Exclude from recursion": "Aus Rekursion ausschließen",
986986 "Prevent further recursion (this entry will not activate others)": "Weitere Rekursion verhindern (dieser Eintrag aktiviert keine anderen)",
987987 "Delay until recursion (this entry can only be activated on recursive checking)": "Verzögerung bis zur Rekursion (dieser Eintrag kann nur bei rekursiver Prüfung aktiviert werden)",
988988 "What this keyword should mean to the AI, sent verbatim": "Was dieses Schlüsselwort für die KI bedeuten soll, wörtlich gesendet",
989989 "Filter to Character(s)": "Filtern auf Charakter(e)",
990990 "Character Exclusion": "Charakterausschluss",
public/locales/es-es.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Ninguno )",
984984 "Content": "Contenido",
985985 "Exclude from recursion": "Excluir de la recursión",
986986 "Prevent further recursion (this entry will not activate others)": "Evitar una mayor recurrencia (esta entrada no activará otras)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "Retraso hasta la recursión (esta entrada solo se puede activar en la comprobación recursiva)",
987+ "Delay until recursion": "Retraso hasta la recursión",
988988 "What this keyword should mean to the AI, sent verbatim": "Lo que este palabra clave debería significar para la IA, enviado textualmente",
989989 "Filter to Character(s)": "Filtrar a Personaje(s)",
990990 "Character Exclusion": "Exclusión de Personaje",
public/locales/fr-fr.json+3 -3
@@ -925,7 +925,7 @@
925925 "Automation ID": "ID d'automatisation",
926926 "( None )": "( Aucun )",
927927 "Content": "Contenu",
928928 "Prevent further recursion (this entry will not activate others)": "Empêcher toute récursion ultérieure (cette entrée n’en activera pas d’autres)",
929929 "What this keyword should mean to the AI, sent verbatim": "Ce que ce mot-clé devrait signifier pour l'IA, envoyé textuellement",
930930 "-- Characters not found --": "-- Personnages non trouvés --",
931931 "Inclusion Group": "Groupe d'inclusion",
@@ -1529,8 +1529,8 @@
15291529 "Group Scoring": "Notation des groupes",
15301530 "delay_until_recursion_level": "Définit les niveaux de délai pour les balayages récursifs.\r\rDans un premier temps, seul le premier niveau (le plus petit nombre) sera pris en compte.\rSi aucune correspondance n'est trouvée, le niveau suivant devient éligible pour la correspondance.\rCette opération est répétée jusqu'à ce que tous les niveaux soient vérifiés.\r\rLié au réglage \"Délai avant récursion\".",
15311531 "Recursion Level": "Niveau de récursivité",
1532- "Non-recursable (will not be activated by another)": "Non récursable (ne peut être activé par un autre)",
1532+ "Non-recursable": "Non récursable",
15331533 "Delay until recursion (can only be activated on recursive checking)": "Délai jusqu'à la récursion (ne peut être activé que lors d'un contrôle récursif)",
15341534 "Prioritize": "Priorités",
15351535 "Sticky entries will stay active for N messages after being triggered.": "Les entrées collantes restent actives pendant N messages après avoir été déclenchées.",
15361536 "Sticky": "Collant",
public/locales/is-is.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Enginn )",
984984 "Content": "Efnisatriði",
985985 "Exclude from recursion": "Útiloka frá endurtekningu",
986986 "Prevent further recursion (this entry will not activate others)": "Koma í veg fyrir frekari endurkomu (þessi færsla mun ekki virkja aðra)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "Seinkað þar til endurkomu (þessi færsla er aðeins hægt að virkja við endurkvæma athugun)",
987+ "Delay until recursion": "Seinkað þar til endurkomu",
988988 "What this keyword should mean to the AI, sent verbatim": "Hvað þetta lykilorð ætti að þýða fyrir AI, sent bókstaflega",
989989 "Filter to Character(s)": "Sía til Persónu(r)",
990990 "Character Exclusion": "Persónuúteslutningur",
public/locales/it-it.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Nessuno )",
984984 "Content": "Contenuto",
985985 "Exclude from recursion": "Escludi dalla ricorsione",
986986 "Prevent further recursion (this entry will not activate others)": "Prevenire ulteriore ricorsione (questa voce non ne attiverà altre)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "Ritardo fino alla ricorsione (questa voce può essere attivata solo durante il controllo ricorsivo)",
987+ "Delay until recursion": "Ritardo fino alla ricorsione",
988988 "What this keyword should mean to the AI, sent verbatim": "Cosa dovrebbe significare questa parola chiave per l'AI, inviata testualmente",
989989 "Filter to Character(s)": "Filtra per Personaggio(i)",
990990 "Character Exclusion": "Esclusione Personaggio",
public/locales/ja-jp.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( なし )",
984984 "Content": "内容",
985985 "Exclude from recursion": "再帰から除外",
986986 "Prevent further recursion (this entry will not activate others)": "それ以上の再帰を防止します(このエントリは他のエントリをアクティブにしません)",
987987 "Delay until recursion (this entry can only be activated on recursive checking)": "再帰まで遅延 (このエントリは再帰チェックでのみ有効になります)",
988988 "What this keyword should mean to the AI, sent verbatim": "このキーワードがAIにとって何を意味するか、そのまま送信されます",
989989 "Filter to Character(s)": "キャラクターにフィルター",
990990 "Character Exclusion": "キャラクターの除外",
public/locales/ko-kr.json+2 -2
@@ -1000,8 +1000,8 @@
10001000 "( None )": "( 없음 )",
10011001 "Content": "콘텐츠",
10021002 "Exclude from recursion": "재귀에서 제외",
10031003 "Prevent further recursion (this entry will not activate others)": "추가 재귀 방지(이 항목은 다른 항목을 활성화하지 않습니다)",
1004- "Delay until recursion (this entry can only be activated on recursive checking)": "재귀까지 지연(이 항목은 재귀 검사에서만 활성화될 수 있음)",
1004+ "Delay until recursion": "재귀까지 지연",
10051005 "What this keyword should mean to the AI, sent verbatim": "AI에게 이 키워드가 무엇을 의미하는지를 원래 그대로 보냄",
10061006 "Filter to Character(s)": "캐릭터로 필터링",
10071007 "Character Exclusion": "캐릭터 제외",
public/locales/nl-nl.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Geen )",
984984 "Content": "Inhoud",
985985 "Exclude from recursion": "Uitsluiten van recursie",
986986 "Prevent further recursion (this entry will not activate others)": "Voorkom verdere recursie (dit item activeert andere niet)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "Vertraging tot recursie (deze invoer kan alleen worden geactiveerd bij recursieve controle)",
987+ "Delay until recursion": "Vertraging tot recursie",
988988 "What this keyword should mean to the AI, sent verbatim": "Wat dit trefwoord voor de AI zou moeten betekenen, woordelijk verzonden",
989989 "Filter to Character(s)": "Filteren op personage(s)",
990990 "Character Exclusion": "Personage uitsluiting",
public/locales/pt-pt.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Nenhum )",
984984 "Content": "Conteúdo",
985985 "Exclude from recursion": "Excluir da recursão",
986986 "Prevent further recursion (this entry will not activate others)": "Impedir mais recursões (esta entrada não ativará outras)",
987987 "Delay until recursion (this entry can only be activated on recursive checking)": "Atraso até a recursão (esta entrada só pode ser ativada na verificação recursiva)",
988988 "What this keyword should mean to the AI, sent verbatim": "O que essa palavra-chave deve significar para a IA, enviada literalmente",
989989 "Filter to Character(s)": "Filtrar para Personagem(s)",
990990 "Character Exclusion": "Exclusão de personagem",
public/locales/ru-ru.json+3 -3
@@ -819,7 +819,7 @@
819819 "Chat CFG": "CFG для чата",
820820 "bg_chat_hint_1": "Здесь будут появляться фоны, сгенерированные расширением",
821821 "bg_chat_hint_2": ".",
822822 "Prevent further recursion (this entry will not activate others)": "Пресечь дальнейшую рекурсию (эта запись не будет активировать другие)",
823823 "Alert if your world info is greater than the allocated budget.": "Оповещать, если ваш мир выходит за выделенный бюджет.",
824824 "Convert to Persona": "Преобразовать в персону",
825825 "Link to Source": "Ссылка на источник",
@@ -2050,8 +2050,8 @@
20502050 "Trigger %": "% срабатывания",
20512051 "Use global": "Глоб. настройка",
20522052 "Whole Words": "Целые слова",
20532053 "Non-recursable (will not be activated by another)": "Не рекурсивная (не активируется другими)",
20542054 "Delay until recursion (can only be activated on recursive checking)": "Рекурсивная (активируется только рекурсией)",
20552055 "Toggle entry's active state.": "Вкл/выкл запись.",
20562056 "Prioritize": "Важная",
20572057 "Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.": "Важная запись получает приоритет среди всех выбранных. Если важных записей несколько, выбирается та, у которой выше \"Очерёдность\".",
public/locales/uk-ua.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Жодного )",
984984 "Content": "Зміст",
985985 "Exclude from recursion": "Виключити з рекурсії",
986986 "Prevent further recursion (this entry will not activate others)": "Запобігти подальшій рекурсії (цей запис не активує інші)",
987- "Delay until recursion (this entry can only be activated on recursive checking)": "Затримка до рекурсії (цей запис можна активувати лише під час рекурсивної перевірки)",
987+ "Delay until recursion": "Затримка до рекурсії",
988988 "What this keyword should mean to the AI, sent verbatim": "Що це ключове слово повинно означати для ШІ, надсилається дослівно",
989989 "Filter to Character(s)": "Фільтр для персонажів",
990990 "Character Exclusion": "Виключення персонажів",
public/locales/vi-vn.json+2 -2
@@ -983,8 +983,8 @@
983983 "( None )": "( Không có )",
984984 "Content": "Nội dung",
985985 "Exclude from recursion": "Loại trừ khỏi đệ quy",
986986 "Prevent further recursion (this entry will not activate others)": "Ngăn chặn đệ quy tiếp theo (mục này sẽ không kích hoạt mục khác)",
987987 "Delay until recursion (this entry can only be activated on recursive checking)": "Trì hoãn cho đến khi đệ quy (mục này chỉ có thể được kích hoạt khi kiểm tra đệ quy)",
988988 "What this keyword should mean to the AI, sent verbatim": "Nghĩa của từ khóa này đối với AI, gửi một cách nguyên văn",
989989 "Filter to Character(s)": "Lọc đến Nhân vật",
990990 "Character Exclusion": "Loại trừ Nhân vật",
public/locales/zh-cn.json+3 -3
@@ -1127,9 +1127,9 @@
11271127 "delay_until_recursion_level": "Defines delay levels for recursive scans.\r\rInitially, only the first level (smallest number) will match.\rOnce no matches are found, the next level becomes eligible for matching.\rThis repeats until all levels are checked.\r\rTied to the \"Delay until recursion\" setting.",
11281128 "Recursion Level": "递归等级",
11291129 "Content": "内容",
11301130 "Non-recursable (will not be activated by another)": "不可递归(不会被其他条目激活)",
11311131 "Prevent further recursion (this entry will not activate others)": "防止进一步递归(本条目将不会激活其他条目)",
11321132 "Delay until recursion (can only be activated on recursive checking)": "延迟到递归(本条目只能在递归检查时激活)",
11331133 "What this keyword should mean to the AI, sent verbatim": "这个关键词对AI的含义,逐字发送",
11341134 "Inclusion Group": "包含组",
11351135 "Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group": "包含组可确保每次仅激活组中的一项(如果触发了多项)。支持多个逗号分隔的组。文档:世界信息 - 包含组",
public/locales/zh-tw.json+4 -5
@@ -983,9 +983,9 @@
983983 "Automation ID": "自動化 ID",
984984 "( None )": "(無)",
985985 "Content": "內容",
986986 "Exclude from recursion": "不可遞迴(此條目不會被其他條目啟用)",
987987 "Prevent further recursion (this entry will not activate others)": "防止進一步遞迴(此條目不會啟用其他條目)",
988988 "Delay until recursion (this entry can only be activated on recursive checking)": "延遲遞迴(此條目只能在遞迴檢查時啟用)",
989989 "What this keyword should mean to the AI, sent verbatim": "這個關鍵字對 AI 應意味著什麼,逐字傳送",
990990 "Filter to Character(s)": "角色篩選",
991991 "Character Exclusion": "角色排除",
@@ -1601,7 +1601,6 @@
16011601 "Data Bank files": "資料庫文件",
16021602 "Default / Fallback Expression": "預設/回退表情",
16031603 "Delay": "延遲",
1604- "Delay until recursion (can only be activated on recursive checking)": "遞迴掃描延遲(僅在啟用遞迴掃描時可用)",
16051604 "Do not proceed if you do not agree to this!": "若不同意此條款,請勿繼續!",
16061605 "Edge Color": "邊緣顏色",
16071606 "Edit captions before saving": "在儲存前編輯註解",
@@ -1681,7 +1680,7 @@
16811680 "Names as Stop Strings": "將名稱用作停止字串",
16821681 "Never": "從不",
16831682 "NomicAI API Key": "NomicAI API 金鑰",
16841683 "Non-recursable (will not be activated by another)": "不可遞迴(不會被其他條目啟動)",
16851684 "None (disabled)": "無(已停用)",
16861685 "OK": "確定",
16871686 "Old messages are vectorized gradually as you chat. To process all previous messages, click the button below.": "舊訊息會在聊天時逐步向量化。\n若要處理所有先前訊息,請點選下方按鈕。",
public/scripts/world-info.js+32 -2
@@ -2481,6 +2481,7 @@ export const originalWIDataKeyMap = {
24812481 'cooldown': 'extensions.cooldown',
24822482 'delay': 'extensions.delay',
24832483 'triggers': 'extensions.triggers',
2484+ 'ignoreBudget': 'extensions.ignore_budget',
24842485};
24852486
24862487/** Checks the state of the current search, and adds/removes the search sorting option accordingly */
@@ -3568,6 +3569,18 @@ export async function getWorldEntry(name, data, entry) {
35683569 .trigger('input', { noSave: true })
35693570 .trigger('change');
35703571
3572+ // Ignore budget
3573+ const ignoreBudgetInput = editTemplate.find('input[name="ignoreBudget"]');
3574+ ignoreBudgetInput.data('uid', entry.uid);
3575+ ignoreBudgetInput.on('input', async function (_, { noSave = false } = {}) {
3576+ const uid = $(this).data('uid');
3577+ const value = $(this).prop('checked');
3578+ data.entries[uid].ignoreBudget = value;
3579+ setWIOriginalDataValue(data, uid, 'extensions.ignore_budget', data.entries[uid].ignoreBudget);
3580+ !noSave && await saveWorldInfo(name, data);
3581+ });
3582+ ignoreBudgetInput.prop('checked', entry.ignoreBudget ?? false).trigger('input', { noSave: true });
3583+
35713584 countTokensDebounced(counter, contentInput.val());
35723585
35733586 editTemplate.find('.inline-drawer-content').css('display', 'none');
@@ -3739,6 +3752,7 @@ export const newWorldInfoEntryDefinition = {
37393752 order: { default: 100, type: 'number' },
37403753 position: { default: 0, type: 'number' },
37413754 disable: { default: false, type: 'boolean' },
3755+ ignoreBudget: { default: false, type: 'boolean' },
37423756 excludeRecursion: { default: false, type: 'boolean' },
37433757 preventRecursion: { default: false, type: 'boolean' },
37443758 matchPersonaDescription: { default: false, type: 'boolean' },
@@ -4522,7 +4536,17 @@ export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData
45224536 console.debug('[WI] --- PROBABILITY CHECKS ---');
45234537 !newEntries.length && console.debug('[WI] No probability checks to do');
45244538
4539+ let ignoresBudget = newEntries.filter(e => e.ignoreBudget).length;
4540+
45254541 for (const entry of newEntries) {
4542+ ignoresBudget -= (entry.ignoreBudget ? 1 : 0);
4543+ if (token_budget_overflowed && !entry.ignoreBudget) {
4544+ if (ignoresBudget > 0) {
4545+ continue;
4546+ }
4547+ break;
4548+ }
4549+
45264550 function verifyProbability() {
45274551 // If we don't need to roll, it's always true
45284552 if (!entry.useProbability || entry.probability === 100) {
@@ -4556,7 +4580,8 @@ export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData
45564580 entry.content = substituteParams(entry.content);
45574581 newContent += `${entry.content}\n`;
45584582
45594583 if (!entry.ignoreBudget && (textToScanTokens + (await getTokenCountAsync(newContent))) >= budget) {
4584+ if (!token_budget_overflowed) {
45604585 console.debug('[WI] --- BUDGET OVERFLOW CHECK ---');
45614586 if (world_info_overflow_alert) {
45624587 console.warn(`[WI] budget of ${budget} reached, stopping after ${allActivatedEntries.size} entries`);
@@ -4565,7 +4590,8 @@ export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData
45654590 console.debug(`[WI] budget of ${budget} reached, stopping after ${allActivatedEntries.size} entries`);
45664591 }
45674592 token_budget_overflowed = true;
4568- break;
4593+ }
4594+ continue;
45694595 }
45704596
45714597 allActivatedEntries.set(`${entry.world}.${entry.uid}`, entry);
@@ -4953,6 +4979,7 @@ function convertAgnaiMemoryBook(inputObj) {
49534979 cooldown: null,
49544980 delay: null,
49554981 triggers: [],
4982+ ignoreBudget: false,
49564983 };
49574984 });
49584985
@@ -4996,6 +5023,7 @@ function convertRisuLorebook(inputObj) {
49965023 cooldown: null,
49975024 delay: null,
49985025 triggers: [],
5026+ ignoreBudget: false,
49995027 };
50005028 });
50015029
@@ -5044,6 +5072,7 @@ function convertNovelLorebook(inputObj) {
50445072 cooldown: null,
50455073 delay: null,
50465074 triggers: [],
5075+ ignoreBudget: false,
50475076 };
50485077 });
50495078
@@ -5101,6 +5130,7 @@ export function convertCharacterBook(characterBook) {
51015130 matchCreatorNotes: entry.extensions?.match_creator_notes ?? false,
51025131 extensions: entry.extensions ?? {},
51035132 triggers: entry.extensions?.triggers || [],
5133+ ignoreBudget: entry.extensions?.ignore_budget ?? false,
51045134 };
51055135 });
51065136
src/endpoints/characters.js+1 -0
@@ -710,6 +710,7 @@ function convertWorldInfoToCharacterBook(name, entries) {
710710 match_scenario: entry.matchScenario ?? false,
711711 match_creator_notes: entry.matchCreatorNotes ?? false,
712712 triggers: entry.triggers ?? [],
713+ ignore_budget: entry.ignoreBudget ?? false,
713714 },
714715 };
715716