diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 4d060b76..ffa7a66d 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -808,16 +808,76 @@ async function importCharacterCardSpec(card:CharacterCardV2Risu|CharacterCardV3, loreExt = charbook.extensions for(const book of charbook.entries){ + let content = book.content + + if(book.use_regex && !book.keys?.[0]?.startsWith('/')){ + book.use_regex = false + } + + //extention migration + const extensions = book.extensions ?? {} + + if(extensions.useProbability && extensions.probability !== undefined && extensions.probability !== 100){ + content = `@@probability ${extensions.probability}\n` + content + delete extensions.useProbability + delete extensions.probability + } + if(extensions.position === 4 && typeof extensions.depth === 'number' && typeof(extensions.role) === 'number'){ + content = `@@depth ${extensions.depth}\n@@role ${['system','user','assistant'][extensions.role]}\n` + content + delete extensions.position + delete extensions.depth + delete extensions.role + } + if(typeof(extensions.selectiveLogic) === 'number' && book.secondary_keys && book.secondary_keys.length > 0){ + switch(extensions.selectiveLogic){ + case 0:{ + //same as default, pass + break + } + case 1:{ + book.selective = false + content = `@@exclude_keys_all ${book.secondary_keys.join(',')}\n` + content + break + } + case 2:{ + book.selective = false + for(const secKey of book.secondary_keys){ + content = `@@exclude_keys ${secKey}\n` + content + } + break + } + case 3:{ + book.selective = false + for(const secKey of book.secondary_keys){ + content = `@@additional_keys ${secKey}\n` + content + } + break + } + } + } + if(typeof extensions.delay === 'number' && extensions.delay > 0){ + content = `@@activate_only_after ${extensions.delay}\n` + content + delete extensions.delay + } + if(extensions.match_whole_words === true){ + content = `@@match_full_word\n` + content + delete extensions.match_whole_words + } + if(extensions.match_whole_words === false){ + content = `@@match_partial_word\n` + content + delete extensions.match_whole_words + } + lorebook.push({ key: book.keys.join(', '), secondkey: book.secondary_keys?.join(', ') ?? '', insertorder: book.insertion_order, comment: book.name ?? book.comment ?? "", - content: book.content, + content: content, mode: "normal", alwaysActive: book.constant ?? false, selective: book.selective ?? false, - extentions: {...book.extensions, risu_case_sensitive: book.case_sensitive}, + extentions: {...extensions, risu_case_sensitive: book.case_sensitive}, activationPercent: book.extensions?.risu_activationPercent, loreCache: book.extensions?.risu_loreCache ?? null, //@ts-ignore @@ -1694,6 +1754,7 @@ interface charBookEntry{ constant?: boolean // if true, always inserted in the prompt (within budget limit) position?: 'before_char' | 'after_char' // whether the entry is placed before or after the character defs case_sensitive?:boolean + use_regex?:boolean } interface RccCardMetaData{ diff --git a/src/ts/process/lorebook.svelte.ts b/src/ts/process/lorebook.svelte.ts index 484d5c3d..f091d9cf 100644 --- a/src/ts/process/lorebook.svelte.ts +++ b/src/ts/process/lorebook.svelte.ts @@ -215,7 +215,7 @@ export async function loadLoreBookV3Prompt(){ const currentChat = char.chats[page].message const loreDepth = char.loreSettings?.scanDepth ?? DBState.db.loreBookDepth const loreToken = char.loreSettings?.tokenBudget ?? DBState.db.loreBookToken - const fullWordMatching = char.loreSettings?.fullWordMatching ?? false + const fullWordMatchingSetting = char.loreSettings?.fullWordMatching ?? false const chatLength = currentChat.length + 1 //includes first message const recursiveScanning = char.loreSettings?.recursiveScanning ?? false let recursiveAdditionalPrompt = '' @@ -225,7 +225,8 @@ export async function loadLoreBookV3Prompt(){ searchDepth:number, regex:boolean fullWordMatching:boolean - recursiveAdditionalPrompt:string + recursiveAdditionalPrompt:string, + all?:boolean }) => { const sliced = messages.slice(messages.length - arg.searchDepth,messages.length) arg.keys = arg.keys.map(key => key.trim()).filter(key => key.length > 0) @@ -257,11 +258,19 @@ export async function loadLoreBookV3Prompt(){ mText = mText.toLocaleLowerCase() mText = mText.replace(/\{\{\/\/(.+?)\}\}/g,'').replace(/\{\{comment:(.+?)\}\}/g,'') + let allMode = arg.all ?? false + let allModeMatched = true + if(arg.fullWordMatching){ const splited = mText.split(' ') for(const key of arg.keys){ if(splited.includes(key.toLocaleLowerCase())){ - return true + if(!allMode){ + return true + } + } + else if(allMode){ + allModeMatched = false } } } @@ -270,10 +279,18 @@ export async function loadLoreBookV3Prompt(){ for(const key of arg.keys){ const realKey = key.toLocaleLowerCase().replace(/ /g,'') if(mText.includes(realKey)){ - return true + if(!allMode){ + return true + } + } + else if(allMode){ + allModeMatched = false } } } + if(allMode && allModeMatched){ + return true + } return false } @@ -310,8 +327,10 @@ export async function loadLoreBookV3Prompt(){ let role:'system'|'user'|'assistant' = 'system' let searchQueries:{ keys:string[], - negative:boolean + negative:boolean, + all?:boolean }[] = [] + let fullWordMatching = fullWordMatchingSetting const content = CCardLib.decorator.parse(fullLore[i].content, (name, arg) => { switch(name){ case 'end':{ @@ -408,6 +427,22 @@ export async function loadLoreBookV3Prompt(){ }) return } + case 'exclude_keys_all':{ + searchQueries.push({ + keys: arg, + negative: true, + all: true + }) + return + } + case 'match_full_word':{ + fullWordMatching = true + return + } + case 'match_partial_word':{ + fullWordMatching = false + return + } case 'is_user_icon':{ //TODO return false @@ -458,7 +493,8 @@ export async function loadLoreBookV3Prompt(){ searchDepth: scanDepth, regex: fullLore[i].useRegex, fullWordMatching: fullWordMatching, - recursiveAdditionalPrompt: recursiveAdditionalPrompt + recursiveAdditionalPrompt: recursiveAdditionalPrompt, + all: query.all }) if(query.negative){ if(result){