feat: add local activation for lorebook (#767)
# PR Checklist - [x] Have you checked if it works normally in all models? *Ignore this if it doesn't use models.* - [x] Have you checked if it works normally in all web, local, and node hosted versions? If it doesn't, have you blocked it in those versions? - [x] Have you added type definitions? # Description This PR adds a toggle to enable lore books specifically for the current chat session. This feature is expected to be particularly useful for simulator-style chatbots as it allows users to select and activate specific lore books for each chat from a pre-configured set. If you have other idea, feel free to give any feedback or suggestions on this.   
This commit is contained in:
@@ -1057,6 +1057,8 @@ export const languageEnglish = {
|
|||||||
paste: "Paste",
|
paste: "Paste",
|
||||||
depth: "Depth",
|
depth: "Depth",
|
||||||
returnCSSError: "Return CSS Error",
|
returnCSSError: "Return CSS Error",
|
||||||
|
alwaysActiveInChat: "Always Active (Current Chat)",
|
||||||
|
childLoreDesc: "This is a copy of Character lore that remains 'Always Active' until removed or manually deactivated in the original.",
|
||||||
thinkingTokens: "Thinking Tokens",
|
thinkingTokens: "Thinking Tokens",
|
||||||
antiServerOverload: "Anti-Server Overload",
|
antiServerOverload: "Anti-Server Overload",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -978,4 +978,6 @@ export const languageKorean = {
|
|||||||
"copy": "복사",
|
"copy": "복사",
|
||||||
"paste": "붙여넣기",
|
"paste": "붙여넣기",
|
||||||
"depth": "깊이",
|
"depth": "깊이",
|
||||||
|
"alwaysActiveInChat": "언제나 활성화 (현재 챗)",
|
||||||
|
"childLoreDesc": "이것은 캐릭터 로어의 복사본이며, 삭제하거나 원본 로어에서 직접 비활성화하기 전에는 '언제나 활성화' 상태로 유지됩니다."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { XIcon, LinkIcon, SunIcon } from "lucide-svelte";
|
import { XIcon, LinkIcon, SunIcon, BookCopyIcon } from "lucide-svelte";
|
||||||
|
import { v4 } from "uuid";
|
||||||
import { language } from "../../../lang";
|
import { language } from "../../../lang";
|
||||||
import type { loreBook } from "../../../ts/storage/database.svelte";
|
import { getCurrentCharacter, getCurrentChat, type loreBook } from "../../../ts/storage/database.svelte";
|
||||||
import { alertConfirm } from "../../../ts/alert";
|
import { alertConfirm, alertMd } from "../../../ts/alert";
|
||||||
import Check from "../../UI/GUI/CheckInput.svelte";
|
import Check from "../../UI/GUI/CheckInput.svelte";
|
||||||
import Help from "../../Others/Help.svelte";
|
import Help from "../../Others/Help.svelte";
|
||||||
import TextInput from "../../UI/GUI/TextInput.svelte";
|
import TextInput from "../../UI/GUI/TextInput.svelte";
|
||||||
@@ -34,10 +35,56 @@
|
|||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
let tokens = $state(0)
|
let tokens = $state(0)
|
||||||
|
|
||||||
|
function isLocallyActivated(book: loreBook){
|
||||||
|
return book.id ? getCurrentChat()?.localLore.some(e => e.id === book.id) : false
|
||||||
|
}
|
||||||
|
function activateLocally(book: loreBook){
|
||||||
|
if(!book.id){
|
||||||
|
book.id = v4()
|
||||||
|
}
|
||||||
|
|
||||||
|
const childLore: loreBook = {
|
||||||
|
key: '',
|
||||||
|
comment: '',
|
||||||
|
content: '',
|
||||||
|
mode: 'child',
|
||||||
|
insertorder: 100,
|
||||||
|
alwaysActive: true,
|
||||||
|
secondkey: '',
|
||||||
|
selective: false,
|
||||||
|
id: book.id,
|
||||||
|
}
|
||||||
|
getCurrentChat().localLore.push(childLore)
|
||||||
|
}
|
||||||
|
function deactivateLocally(book: loreBook){
|
||||||
|
if(!book.id) return
|
||||||
|
const chat = getCurrentChat()
|
||||||
|
const childLore = chat?.localLore?.find(e => e.id === book.id)
|
||||||
|
if(childLore){
|
||||||
|
chat.localLore = chat.localLore.filter(e => e.id !== book.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toggleLocalActive(check: boolean, book: loreBook){
|
||||||
|
if(check){
|
||||||
|
activateLocally(book)
|
||||||
|
}else{
|
||||||
|
deactivateLocally(book)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getParentLoreName(book: loreBook){
|
||||||
|
if(book.mode === 'child'){
|
||||||
|
const value = getCurrentCharacter()?.globalLore.find(e => e.id === book.id)
|
||||||
|
if(value){
|
||||||
|
return value.comment.length === 0 ? value.key.length === 0 ? "Unnamed Lore" : value.key : value.comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full flex flex-col pt-2 mt-2 border-t border-t-selected first:pt-0 first:mt-0 first:border-0" data-risu-idx={idx}>
|
<div class="w-full flex flex-col pt-2 mt-2 border-t border-t-selected first:pt-0 first:mt-0 first:border-0" data-risu-idx={idx}>
|
||||||
<div class="flex items-center transition-colors w-full p-1">
|
<div class="flex items-center transition-colors w-full p-1">
|
||||||
|
{#if value.mode !== 'child'}
|
||||||
<button class="endflex valuer border-darkborderc" onclick={() => {
|
<button class="endflex valuer border-darkborderc" onclick={() => {
|
||||||
value.secondkey = value.secondkey ?? ''
|
value.secondkey = value.secondkey ?? ''
|
||||||
open = !open
|
open = !open
|
||||||
@@ -70,11 +117,29 @@
|
|||||||
if(!open){
|
if(!open){
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
deactivateLocally(value)
|
||||||
onRemove()
|
onRemove()
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<XIcon size={20} />
|
<XIcon size={20} />
|
||||||
</button>
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button class="endflex valuer border-darkborderc" onclick={() => alertMd(language.childLoreDesc)}>
|
||||||
|
<BookCopyIcon size={20} class="mr-1" />
|
||||||
|
<span>{getParentLoreName(value)}</span>
|
||||||
|
</button>
|
||||||
|
<button class="valuer" onclick={async () => {
|
||||||
|
const d = await alertConfirm(language.removeConfirm + getParentLoreName(value))
|
||||||
|
if(d){
|
||||||
|
if(!open){
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
onRemove()
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<XIcon size={20} />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if open}
|
{#if open}
|
||||||
<div class="border-0 outline-none w-full mt-2 flex flex-col mb-2">
|
<div class="border-0 outline-none w-full mt-2 flex flex-col mb-2">
|
||||||
@@ -120,6 +185,11 @@
|
|||||||
<div class="flex items-center mt-4">
|
<div class="flex items-center mt-4">
|
||||||
<Check bind:check={value.alwaysActive} name={language.alwaysActive}/>
|
<Check bind:check={value.alwaysActive} name={language.alwaysActive}/>
|
||||||
</div>
|
</div>
|
||||||
|
{#if !value.alwaysActive && getCurrentCharacter()?.globalLore?.includes(value)}
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<Check check={isLocallyActivated(value)} onChange={(check: boolean) => toggleLocalActive(check, value)} name={language.alwaysActiveInChat}/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{#if !lorePlus && !value.useRegex}
|
{#if !lorePlus && !value.useRegex}
|
||||||
<div class="flex items-center mt-2">
|
<div class="flex items-center mt-2">
|
||||||
<Check bind:check={value.selective} name={language.selective}/>
|
<Check bind:check={value.selective} name={language.selective}/>
|
||||||
|
|||||||
@@ -218,6 +218,21 @@ export async function loadLoreBookV3Prompt(){
|
|||||||
all?:boolean
|
all?:boolean
|
||||||
}[] = []
|
}[] = []
|
||||||
let fullWordMatching = fullWordMatchingSetting
|
let fullWordMatching = fullWordMatchingSetting
|
||||||
|
|
||||||
|
if(fullLore[i].mode === 'child'){
|
||||||
|
activated = false
|
||||||
|
for(let j=0;j<i;j++){
|
||||||
|
if(fullLore[j].id === fullLore[i].id){
|
||||||
|
if(!activatedIndexes.includes(j)){
|
||||||
|
fullLore[i].comment = fullLore[j].comment
|
||||||
|
fullLore[i].content = fullLore[j].content
|
||||||
|
fullLore[i].alwaysActive = true
|
||||||
|
activated = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const content = CCardLib.decorator.parse(fullLore[i].content, (name, arg) => {
|
const content = CCardLib.decorator.parse(fullLore[i].content, (name, arg) => {
|
||||||
switch(name){
|
switch(name){
|
||||||
case 'end':{
|
case 'end':{
|
||||||
|
|||||||
@@ -956,7 +956,7 @@ export interface loreBook{
|
|||||||
insertorder: number
|
insertorder: number
|
||||||
comment: string
|
comment: string
|
||||||
content: string
|
content: string
|
||||||
mode: 'multiple'|'constant'|'normal',
|
mode: 'multiple'|'constant'|'normal'|'child',
|
||||||
alwaysActive: boolean
|
alwaysActive: boolean
|
||||||
selective:boolean
|
selective:boolean
|
||||||
extentions?:{
|
extentions?:{
|
||||||
@@ -969,6 +969,7 @@ export interface loreBook{
|
|||||||
},
|
},
|
||||||
useRegex?:boolean
|
useRegex?:boolean
|
||||||
bookVersion?:number
|
bookVersion?:number
|
||||||
|
id?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface character{
|
export interface character{
|
||||||
|
|||||||
Reference in New Issue
Block a user