diff --git a/package.json b/package.json index 5cb0652f..5f4b04e4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@smithy/protocol-http": "^3.0.12", "@smithy/signature-v4": "^2.0.19", "@tauri-apps/api": "1.5.3", - "@xenova/transformers": "^2.14.0", + "@xenova/transformers": "^2.17.1", "blueimp-md5": "^2.19.0", "body-parser": "^1.20.2", "buffer": "^6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a715ae3..79e98d97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,8 +39,8 @@ dependencies: specifier: 1.5.3 version: 1.5.3 '@xenova/transformers': - specifier: ^2.14.0 - version: 2.14.0 + specifier: ^2.17.1 + version: 2.17.1 blueimp-md5: specifier: ^2.19.0 version: 2.19.0 @@ -701,11 +701,6 @@ packages: dev: true optional: true - /@huggingface/jinja@0.1.2: - resolution: {integrity: sha512-x5mpbfJt1nKmVep5WNP5VjNsjWApWNj8pPYI+uYMkBWH9bWUJmQmHt2lbf0VCoQd54Oq3XuFEh/UyoVh7rPxmg==} - engines: {node: '>=18'} - dev: false - /@huggingface/jinja@0.2.2: resolution: {integrity: sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==} engines: {node: '>=18'} @@ -1704,10 +1699,10 @@ packages: resolution: {integrity: sha512-ggMz8nOygG7d/stpH40WVaNvBwuyYLnrg5Mbyf6bmsj/8+gb6Ei4ZZ9/4PNpcPNTT8th9Q8sM8wYmWGjMWLX/A==} dev: true - /@xenova/transformers@2.14.0: - resolution: {integrity: sha512-rQ3O7SW5EM64b6XFZGx3XQ2cfiroefxUwU9ShfSpEZyhd082GvwNJJKndxgaukse1hZP1JUDoT0DfjDiq4IZiw==} + /@xenova/transformers@2.17.1: + resolution: {integrity: sha512-zo702tQAFZXhzeD2GCYUNUqeqkoueOdiSbQWa4s0q7ZE4z8WBIwIsMMPGobpgdqjQ2u0Qulo08wuqVEUrBXjkQ==} dependencies: - '@huggingface/jinja': 0.1.2 + '@huggingface/jinja': 0.2.2 onnxruntime-web: 1.14.0 sharp: 0.32.6 optionalDependencies: diff --git a/src/lang/en.ts b/src/lang/en.ts index 058171bc..9650607b 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -579,4 +579,5 @@ export const languageEnglish = { tokenizer: "Tokenizer", chatFormating: "Chat Formating", useInstructPrompt: "Use Instruction Prompt", + hanuraiMemory: "HanuraiMemory", } \ No newline at end of file diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index b0850e54..67c98344 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -207,32 +207,63 @@ - - {language.SuperMemory} {language.model} - + + {language.type} + + { + //@ts-ignore + const value = v.target.value + if (value === 'supaMemory'){ + $DataBase.supaMemoryType = 'distilbart' + $DataBase.hanuraiEnable = false + } else if (value === 'hanuraiMemory'){ + $DataBase.supaMemoryType = 'none' + $DataBase.hanuraiEnable = true + } else { + $DataBase.supaMemoryType = 'none' + $DataBase.hanuraiEnable = false + } + }}> None - distilbart-cnn-6-6 (Free/Local) - OpenAI 3.5 Turbo Instruct - {language.submodel} + {language.SuperMemory} + {language.hanuraiMemory} - {language.maxSupaChunkSize} - - {#if $DataBase.supaMemoryType === 'davinci' || $DataBase.supaMemoryType === 'curie' || $DataBase.supaMemoryType === 'instruct35'} - {language.SuperMemory} OpenAI Key - - {/if} - {#if $DataBase.supaMemoryType !== 'none'} - {language.SuperMemory} Prompt - - {/if} - {#if $DataBase.hypaMemory} - {language.HypaMemory} Model - - MiniLM-L6-v2 (Free / Local) - OpenAI Ada (Davinci / Curie Only) + + {#if $DataBase.hanuraiEnable} + Chunk Size + +
+ +
+ {:else if $DataBase.supaMemoryType !== 'none'} + {language.SuperMemory} {language.model} + + distilbart-cnn-6-6 (Free/Local) + OpenAI 3.5 Turbo Instruct + {language.submodel} + {language.maxSupaChunkSize} + + {#if $DataBase.supaMemoryType === 'davinci' || $DataBase.supaMemoryType === 'curie' || $DataBase.supaMemoryType === 'instruct35'} + {language.SuperMemory} OpenAI Key + + {/if} + {#if $DataBase.supaMemoryType !== 'none'} + {language.SuperMemory} Prompt + + {/if} + {#if $DataBase.hypaMemory} + {language.HypaMemory} Model + + MiniLM-L6-v2 (Free / Local) + OpenAI Ada (Davinci / Curie Only) + + {/if} +
+ +
{/if} -
- -
\ No newline at end of file diff --git a/src/ts/process/index.ts b/src/ts/process/index.ts index 914c09c1..a143e88e 100644 --- a/src/ts/process/index.ts +++ b/src/ts/process/index.ts @@ -26,6 +26,7 @@ import { runInlayScreen } from "./inlayScreen"; import { runCharacterJS } from "../plugins/embedscript"; import { addRerolls } from "./prereroll"; import { runImageEmbedding } from "./transformers"; +import { hanuraiMemory } from "./memory/hanuraiMemory"; export interface OpenAIChat{ role: 'system'|'user'|'assistant'|'function' @@ -647,22 +648,39 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n index++ } - if(nowChatroom.supaMemory && db.supaMemoryType !== 'none'){ + + if(nowChatroom.supaMemory && (db.supaMemoryType !== 'none' || db.hanuraiEnable)){ chatProcessStage.set(2) - const sp = await supaMemory(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer, { - asHyper: db.hypaMemory - }) - if(sp.error){ - alertError(sp.error) - return false + if(db.hanuraiEnable){ + const hn = await hanuraiMemory(chats, { + currentTokens, + maxContextTokens, + tokenizer + }) + + if(hn === false){ + return false + } + + chats = hn.chats + currentTokens = hn.tokens + } + else{ + const sp = await supaMemory(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer, { + asHyper: db.hypaMemory + }) + if(sp.error){ + alertError(sp.error) + return false + } + chats = sp.chats + currentTokens = sp.currentTokens + currentChat.supaMemoryData = sp.memory ?? currentChat.supaMemoryData + db.characters[selectedChar].chats[selectedChat].supaMemoryData = currentChat.supaMemoryData + console.log(currentChat.supaMemoryData) + DataBase.set(db) + currentChat.lastMemory = sp.lastId ?? currentChat.lastMemory; } - chats = sp.chats - currentTokens = sp.currentTokens - currentChat.supaMemoryData = sp.memory ?? currentChat.supaMemoryData - db.characters[selectedChar].chats[selectedChat].supaMemoryData = currentChat.supaMemoryData - console.log(currentChat.supaMemoryData) - DataBase.set(db) - currentChat.lastMemory = sp.lastId ?? currentChat.lastMemory chatProcessStage.set(1) } else{ diff --git a/src/ts/process/memory/hanuraiMemory.ts b/src/ts/process/memory/hanuraiMemory.ts new file mode 100644 index 00000000..cf14e997 --- /dev/null +++ b/src/ts/process/memory/hanuraiMemory.ts @@ -0,0 +1,94 @@ +import { alertError } from "src/ts/alert"; +import type { OpenAIChat } from ".."; +import { HypaProcesser } from "./hypamemory"; +import { language } from "src/lang"; +import type { ChatTokenizer } from "src/ts/tokenizer"; +import { get } from "svelte/store"; +import { DataBase } from "src/ts/storage/database"; + +export async function hanuraiMemory(chats:OpenAIChat[],arg:{ + currentTokens:number, + maxContextTokens:number, + tokenizer:ChatTokenizer +}){ + const db = get(DataBase) + const tokenizer = arg.tokenizer + const processer = new HypaProcesser('nomic') + let addTexts:string[] = [] + chats.map((chat) => { + if(!chat?.content?.trim()){ + return + } + if(db.hanuraiSplit){ + const splited = chat.content.split('\n\n') + for(const split of splited){ + if(!split.trim()){ + continue + } + addTexts.push(`search_document: ${split.trim()}`) + } + } + addTexts.push(`search_document: ${chat.content?.trim()}`) + }) + processer.addText(addTexts) + + let scoredResults:{[key:string]:number} = {} + for(let i=1;i<5;i++){ + const chat = chats[chats.length-i] + if(!chat?.content){ + continue + } + const scoredArray = (await processer.similaritySearchScored('search_query: ' + chat.content)).map((result) => { + return [result[0],result[1]/i] as [string,number] + }) + for(const scored of scoredArray){ + if(scoredResults[scored[0]]){ + scoredResults[scored[0]] += scored[1] + }else{ + scoredResults[scored[0]] = scored[1] + } + } + } + const vectorResult = Object.entries(scoredResults).sort((a,b)=>a[1]-b[1]) + + + let tokens = arg.currentTokens + db.hanuraiTokens + + while(tokens < arg.maxContextTokens){ + const poped = chats.pop() + if(!poped){ + alertError(language.errors.toomuchtoken + "\n\nRequired Tokens: " + tokens) + return false + } + tokens -= await tokenizer.tokenizeChat(chats[0]) + } + + tokens -= db.hanuraiTokens + + let resultTexts:string[] = [] + for(const vector of vectorResult){ + const chat = chats.find((chat) => chat.content === vector[0].substring(14)) + if(chat){ + continue + } + const tokenized = await tokenizer.tokenizeChat(chat) + 2 + tokens += tokenized + if(tokens >= arg.maxContextTokens){ + tokens -= tokenized + break + } + resultTexts.push(vector[0].substring(14)) + } + console.log(resultTexts) + chats.unshift({ + role: "system", + memo: "supaMemory", + content: resultTexts.join('\n\n'), + }) + return { + tokens, + chats + } + + +} \ No newline at end of file diff --git a/src/ts/process/memory/hypamemory.ts b/src/ts/process/memory/hypamemory.ts index 9904885a..f4a89cc7 100644 --- a/src/ts/process/memory/hypamemory.ts +++ b/src/ts/process/memory/hypamemory.ts @@ -92,7 +92,7 @@ export class HypaProcesser{ async addText(texts:string[]) { for(let i=0;ichat.content)) - - let scoredResults:{[key:string]:number} - for(let i=1;i<5;i++){ - const chat = chats[chats.length-i] - if(!chat?.content){ - continue - } - const scoredArray = (await processer.similaritySearchScored(chat.content)).map((result) => { - return [result[0],result[1]/i] as [string,number] - }) - for(const scored of scoredArray){ - if(scoredResults[scored[0]]){ - scoredResults[scored[0]] += scored[1] - }else{ - scoredResults[scored[0]] = scored[1] - } - } - } - const result = Object.entries(scoredResults).sort((a,b)=>a[1]-b[1]) - return result.map(([content,score])=>(content)).join('\n\n') - -} \ No newline at end of file diff --git a/src/ts/process/transformers.ts b/src/ts/process/transformers.ts index 1c64a547..533398e3 100644 --- a/src/ts/process/transformers.ts +++ b/src/ts/process/transformers.ts @@ -50,7 +50,8 @@ export const runSummarizer = async (text: string) => { } let extractor:FeatureExtractionPipeline = null -export const runEmbedding = async (text: string, model:'Xenova/all-MiniLM-L6-v2'|'nomic-ai/nomic-embed-text-v1.5' = 'Xenova/all-MiniLM-L6-v2'):Promise => { +type EmbeddingModel = 'Xenova/all-MiniLM-L6-v2'|'nomic-ai/nomic-embed-text-v1.5' +export const runEmbedding = async (text: string, model:EmbeddingModel = 'Xenova/all-MiniLM-L6-v2'):Promise => { await initTransformers() if(!extractor){ extractor = await pipeline('feature-extraction', model); diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index d34a14cc..3e1dd30e 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -394,6 +394,9 @@ export function setDatabase(data:Database){ data.instructChatTemplate ??= "chatml" data.openrouterProvider ??= '' data.useInstructPrompt ??= false + data.hanuraiEnable ??= false + data.hanuraiSplit ??= true + data.hanuraiTokens ??= 1000 changeLanguage(data.language) DataBase.set(data) @@ -642,6 +645,9 @@ export interface Database{ JinjaTemplate:string openrouterProvider:string useInstructPrompt:boolean + hanuraiTokens:number + hanuraiSplit:boolean + hanuraiEnable:boolean } export interface customscript{ @@ -847,7 +853,6 @@ export interface botPreset{ top_a?:number openrouterProvider?:string useInstructPrompt?:boolean - }