[feat] lorebook plus
This commit is contained in:
@@ -100,6 +100,7 @@ export const languageEnglish = {
|
||||
oaiRandomUser: "If enabled, random uuid would be put on user parameter on request, and would be changed on refresh. this can be used to prevent AI from identifying user.",
|
||||
inlayImages: "If enabled, images could be inlayed to the chat and AIs can see it if they support it.",
|
||||
metrica: 'Metric Systemizer is a plugin that converts metrics to imperial units when request, and vice versa on output to show user metric system while using imperial for performace. it is not recommended to use this plugin when using imperial units on chat.',
|
||||
lorePlus: "LoreBook+ is a experimental feature that uses vectordb instead of just string matching for better bot making experience and better matching performace."
|
||||
},
|
||||
setup: {
|
||||
chooseProvider: "Choose AI Provider",
|
||||
@@ -447,4 +448,5 @@ export const languageEnglish = {
|
||||
depthPrompt: "Depth Prompt",
|
||||
largePortrait: "Portrait",
|
||||
postImage: "Post Image",
|
||||
lorePlus: "LoreBook+",
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
export let onRemove: () => void = () => {}
|
||||
export let onClose: () => void = () => {}
|
||||
export let onOpen: () => void = () => {}
|
||||
export let lorePlus = false
|
||||
|
||||
export let idx:number
|
||||
let open = false
|
||||
@@ -47,47 +48,57 @@
|
||||
<div class="border-0 outline-none w-full mt-2 flex flex-col mb-2">
|
||||
<span class="text-textcolor mt-6">{language.name} <Help key="loreName"/></span>
|
||||
<TextInput size="sm" bind:value={value.comment}/>
|
||||
{#if !value.alwaysActive}
|
||||
<span class="text-textcolor mt-6">{language.activationKeys} <Help key="loreActivationKey"/></span>
|
||||
<span class="text-xs text-textcolor2">{language.activationKeysInfo}</span>
|
||||
<TextInput size="sm" bind:value={value.key}/>
|
||||
|
||||
{#if value.selective}
|
||||
<span class="text-textcolor mt-6">{language.SecondaryKeys}</span>
|
||||
{#if !lorePlus}
|
||||
{#if !value.alwaysActive}
|
||||
<span class="text-textcolor mt-6">{language.activationKeys} <Help key="loreActivationKey"/></span>
|
||||
<span class="text-xs text-textcolor2">{language.activationKeysInfo}</span>
|
||||
<TextInput size="sm" bind:value={value.secondkey}/>
|
||||
<TextInput size="sm" bind:value={value.key}/>
|
||||
|
||||
{#if value.selective}
|
||||
<span class="text-textcolor mt-6">{language.SecondaryKeys}</span>
|
||||
<span class="text-xs text-textcolor2">{language.activationKeysInfo}</span>
|
||||
<TextInput size="sm" bind:value={value.secondkey}/>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !(value.activationPercent === undefined || value.activationPercent === null)}
|
||||
<span class="text-textcolor mt-6">{language.activationProbability}</span>
|
||||
<NumberInput size="sm" bind:value={value.activationPercent} onChange={() => {
|
||||
if(isNaN(value.activationPercent) || !value.activationPercent || value.activationPercent < 0){
|
||||
value.activationPercent = 0
|
||||
}
|
||||
if(value.activationPercent > 100){
|
||||
value.activationPercent = 100
|
||||
}
|
||||
}} />
|
||||
{#if !lorePlus}
|
||||
{#if !(value.activationPercent === undefined || value.activationPercent === null)}
|
||||
<span class="text-textcolor mt-6">{language.activationProbability}</span>
|
||||
<NumberInput size="sm" bind:value={value.activationPercent} onChange={() => {
|
||||
if(isNaN(value.activationPercent) || !value.activationPercent || value.activationPercent < 0){
|
||||
value.activationPercent = 0
|
||||
}
|
||||
if(value.activationPercent > 100){
|
||||
value.activationPercent = 100
|
||||
}
|
||||
}} />
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !lorePlus}
|
||||
<span class="text-textcolor mt-4">{language.insertOrder} <Help key="loreorder"/></span>
|
||||
<NumberInput size="sm" bind:value={value.insertorder} min={0} max={1000}/>
|
||||
{/if}
|
||||
<span class="text-textcolor mt-4">{language.insertOrder} <Help key="loreorder"/></span>
|
||||
<NumberInput size="sm" bind:value={value.insertorder} min={0} max={1000}/>
|
||||
<span class="text-textcolor mt-4 mb-2">{language.prompt}</span>
|
||||
<TextAreaInput autocomplete="off" bind:value={value.content} />
|
||||
<div class="flex items-center mt-4">
|
||||
<Check bind:check={value.alwaysActive} name={language.alwaysActive}/>
|
||||
</div>
|
||||
<div class="flex items-center mt-2">
|
||||
<Check bind:check={value.selective} name={language.selective}/>
|
||||
<Help key="loreSelective" name={language.selective}/>
|
||||
</div>
|
||||
<div class="flex items-center mt-2 mb-6">
|
||||
{#if value.activationPercent === undefined || value.activationPercent === null}
|
||||
<Check name={language.loreRandomActivation} check={false} onChange={() => {value.activationPercent = 50}}/>
|
||||
{:else}
|
||||
<Check name={language.loreRandomActivation} check={true} onChange={() => {value.activationPercent = null}}/>
|
||||
{/if}
|
||||
<span><Help name={language.loreRandomActivation} key="loreRandomActivation"/></span>
|
||||
</div>
|
||||
{#if !lorePlus}
|
||||
<div class="flex items-center mt-2">
|
||||
<Check bind:check={value.selective} name={language.selective}/>
|
||||
<Help key="loreSelective" name={language.selective}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !lorePlus}
|
||||
<div class="flex items-center mt-2 mb-6">
|
||||
{#if value.activationPercent === undefined || value.activationPercent === null}
|
||||
<Check name={language.loreRandomActivation} check={false} onChange={() => {value.activationPercent = 50}}/>
|
||||
{:else}
|
||||
<Check name={language.loreRandomActivation} check={true} onChange={() => {value.activationPercent = null}}/>
|
||||
{/if}
|
||||
<span><Help name={language.loreRandomActivation} key="loreRandomActivation"/></span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
export let globalMode = false
|
||||
export let submenu = 0
|
||||
export let lorePlus = false
|
||||
let stb: Sortable = null
|
||||
let ele: HTMLDivElement
|
||||
let sorted = 0
|
||||
@@ -94,7 +95,7 @@
|
||||
let lore = $CurrentCharacter.globalLore
|
||||
lore.splice(i, 1)
|
||||
$CurrentCharacter.globalLore = lore
|
||||
}} onOpen={onOpen} onClose={onClose}/>
|
||||
}} onOpen={onOpen} onClose={onClose} lorePlus={lorePlus}/>
|
||||
{/each}
|
||||
{/if}
|
||||
{:else if submenu === 1}
|
||||
@@ -106,7 +107,7 @@
|
||||
let lore = $CurrentChat.localLore
|
||||
lore.splice(i, 1)
|
||||
$CurrentChat.localLore = lore
|
||||
}} onOpen={onOpen} onClose={onClose}/>
|
||||
}} onOpen={onOpen} onClose={onClose} lorePlus={lorePlus}/>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import Check from "../../UI/GUI/CheckInput.svelte";
|
||||
import NumberInput from "../../UI/GUI/NumberInput.svelte";
|
||||
import LoreBookList from "./LoreBookList.svelte";
|
||||
import Help from "src/lib/Others/Help.svelte";
|
||||
|
||||
let submenu = 0
|
||||
export let globalMode = false
|
||||
@@ -35,7 +36,7 @@
|
||||
{#if !globalMode}
|
||||
<span class="text-textcolor2 mt-2 mb-6 text-sm">{submenu === 0 ? $CurrentCharacter.type === 'group' ? language.groupLoreInfo : language.globalLoreInfo : language.localLoreInfo}</span>
|
||||
{/if}
|
||||
<LoreBookList bind:globalMode bind:submenu />
|
||||
<LoreBookList bind:globalMode bind:submenu lorePlus={$CurrentCharacter.lorePlus} />
|
||||
{:else}
|
||||
{#if $CurrentCharacter.loreSettings}
|
||||
<div class="flex items-center mt-4">
|
||||
@@ -68,6 +69,11 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex items-center mt-4">
|
||||
<Check bind:check={$CurrentCharacter.lorePlus}
|
||||
name={language.lorePlus}
|
||||
><Help key="lorePlus"></Help><Help key="experimental"></Help></Check>
|
||||
</div>
|
||||
{/if}
|
||||
{#if submenu !== 2}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { checkNullish, selectSingleFile } from "../util";
|
||||
import { alertError, alertNormal } from "../alert";
|
||||
import { language } from "../../lang";
|
||||
import { downloadFile } from "../storage/globalApi";
|
||||
import { HypaProcesser } from "./memory/hypamemory";
|
||||
|
||||
export function addLorebook(type:number) {
|
||||
let selectedID = get(selectedCharID)
|
||||
@@ -61,6 +62,8 @@ interface formatedLore{
|
||||
const rmRegex = / |\n/g
|
||||
|
||||
export async function loadLoreBookPrompt(){
|
||||
|
||||
|
||||
const selectedID = get(selectedCharID)
|
||||
const db = get(DataBase)
|
||||
const char = db.characters[selectedID]
|
||||
@@ -74,6 +77,10 @@ export async function loadLoreBookPrompt(){
|
||||
const loreToken = char.loreSettings?.tokenBudget ?? db.loreBookToken
|
||||
const fullWordMatching = char.loreSettings?.fullWordMatching ?? false
|
||||
|
||||
if(char.lorePlus){
|
||||
return await loadLoreBookPlusPrompt()
|
||||
}
|
||||
|
||||
let activatiedPrompt: string[] = []
|
||||
|
||||
let formatedLore:formatedLore[] = []
|
||||
@@ -201,6 +208,94 @@ export async function loadLoreBookPrompt(){
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadLoreBookPlusPrompt(){
|
||||
const selectedID = get(selectedCharID)
|
||||
const db = get(DataBase)
|
||||
const char = db.characters[selectedID]
|
||||
const page = char.chatPage
|
||||
const characterLore = char.globalLore ?? []
|
||||
const chatLore = char.chats[page].localLore ?? []
|
||||
const globalLore = db.loreBook[db.loreBookPage]?.data ?? []
|
||||
const fullLore = characterLore.concat(chatLore.concat(globalLore)).filter((v) => { return v.content })
|
||||
const currentChat = char.chats[page].message
|
||||
const loreDepth = char.loreSettings?.scanDepth ?? db.loreBookDepth
|
||||
const loreToken = char.loreSettings?.tokenBudget ?? db.loreBookToken
|
||||
|
||||
interface formatedLorePlus{
|
||||
content: string
|
||||
simularity:number
|
||||
}
|
||||
|
||||
let formatedLores:formatedLorePlus[] = []
|
||||
let activatiedPrompt: string[] = []
|
||||
const hypaProcesser = new HypaProcesser('MiniLM')
|
||||
|
||||
|
||||
const formatedChatMain = currentChat.slice(currentChat.length - loreDepth,currentChat.length).map((msg) => {
|
||||
return msg.data
|
||||
}).join('||').replace(rmRegex,'').toLocaleLowerCase()
|
||||
const chatVec = await hypaProcesser.testText(formatedChatMain)
|
||||
|
||||
|
||||
for(const lore of fullLore){
|
||||
let key = (lore.key ?? '').replace(rmRegex, '').toLocaleLowerCase().split(',')
|
||||
key.push(lore.comment)
|
||||
|
||||
let vec:number[]
|
||||
|
||||
if(lore.loreCache && lore.loreCache.key === lore.content){
|
||||
const vect = lore.loreCache.data[0]
|
||||
const v = Buffer.from(vect, 'base64')
|
||||
const f = new Float32Array(v.buffer)
|
||||
vec = Array.from(f)
|
||||
}
|
||||
else{
|
||||
vec = await hypaProcesser.testText(lore.content)
|
||||
lore.loreCache = {
|
||||
key: lore.content,
|
||||
data: [Buffer.from(new Float32Array(vec).buffer).toString('base64')]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
formatedLores.push({
|
||||
content: lore.content,
|
||||
simularity: hypaProcesser.similarityCheck(chatVec, vec)
|
||||
})
|
||||
}
|
||||
|
||||
formatedLores.sort((a, b) => {
|
||||
return b.simularity - a.simularity
|
||||
})
|
||||
|
||||
let i=0;
|
||||
while(i < formatedLores.length){
|
||||
const lore = formatedLores[i]
|
||||
const totalTokens = await tokenize(activatiedPrompt.concat([lore.content]).join('\n\n'))
|
||||
if(totalTokens > loreToken){
|
||||
break
|
||||
}
|
||||
activatiedPrompt.push(lore.content)
|
||||
i++
|
||||
}
|
||||
|
||||
|
||||
let sactivated:string[] = []
|
||||
activatiedPrompt = activatiedPrompt.filter((v) => {
|
||||
if(v.startsWith("@@@end")){
|
||||
sactivated.push(v.replace('@@@end','').trim())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return {
|
||||
act: activatiedPrompt.reverse().join('\n\n'),
|
||||
special_act: sactivated.reverse().join('\n\n')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export async function importLoreBook(mode:'global'|'local'|'sglobal'){
|
||||
const selectedID = get(selectedCharID)
|
||||
|
||||
@@ -79,7 +79,15 @@ export class HypaProcesser{
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
async testText(text:string){
|
||||
const forageResult:number[] = await this.forage.getItem(text)
|
||||
if(forageResult){
|
||||
return forageResult
|
||||
}
|
||||
const vec = (await this.embedDocuments([text]))[0]
|
||||
await this.forage.setItem(text, vec)
|
||||
return vec
|
||||
}
|
||||
|
||||
async addText(texts:string[]) {
|
||||
|
||||
@@ -147,7 +155,11 @@ export class HypaProcesser{
|
||||
]);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
similarityCheck(query1:number[],query2: number[]) {
|
||||
return similarity.cosine(query1, query2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -526,6 +526,10 @@ export interface loreBook{
|
||||
risu_case_sensitive:boolean
|
||||
}
|
||||
activationPercent?:number
|
||||
loreCache?:{
|
||||
key:string
|
||||
data:string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface character{
|
||||
@@ -590,6 +594,7 @@ export interface character{
|
||||
depth_prompt?: { depth: number, prompt: string }
|
||||
extentions?:{[key:string]:any}
|
||||
largePortrait?:boolean
|
||||
lorePlus?:boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -632,6 +637,7 @@ export interface groupChat{
|
||||
backgroundCSS?:string
|
||||
oneAtTime?:boolean
|
||||
virtualscript?:string
|
||||
lorePlus?:boolean
|
||||
}
|
||||
|
||||
export interface botPreset{
|
||||
|
||||
Reference in New Issue
Block a user