[feat] lorebook plus

This commit is contained in:
kwaroran
2023-11-13 13:04:14 +09:00
parent a0923f0ae5
commit c3f422104a
7 changed files with 170 additions and 37 deletions

View File

@@ -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+",
}

View File

@@ -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,6 +48,7 @@
<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 !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>
@@ -58,6 +60,8 @@
<TextInput size="sm" bind:value={value.secondkey}/>
{/if}
{/if}
{/if}
{#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={() => {
@@ -69,17 +73,23 @@
}
}} />
{/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 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>
{#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}}/>
@@ -88,6 +98,7 @@
{/if}
<span><Help name={language.loreRandomActivation} key="loreRandomActivation"/></span>
</div>
{/if}
</div>
{/if}
</div>

View File

@@ -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}

View File

@@ -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}

View File

@@ -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)

View File

@@ -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[]) {
@@ -148,6 +156,10 @@ export class HypaProcesser{
return result;
}
similarityCheck(query1:number[],query2: number[]) {
return similarity.cosine(query1, query2)
}
}

View File

@@ -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{