HyperV2 Custom settings enhancement (#511)
# PR Checklist - [x] Did you check if it works normally in all models? *ignore this when it dosen't uses models* - [x] Did you check if it works normally in all of web, local and node hosted versions? if it dosen't, did you blocked it in those versions? - [x] Did you added a type def? # Description ~No bug, work as intended. But, haven't checked actual summary with it. Very unstable, need confirmation/check.~ ### **NO bug, work as intended, summarizes correctly, moving mainChunks to chunks works well, no re-summarization issues. It works!** Added new variable to database.ts, which indicates if HypaV2 Memory is activated or not. hypav2: boolean This was added to stop overlapping of memoryType, when HypaV2 Memory is selected and supaMemoryModel is changed. Added supaMemoryType selection, HypaMemoryType selection, supaMemoryPrompt changing section on OtherBotSettings.svelte, and implemented on hypav2.ts. Also added OpenAI key when summarization model is GPT 3.5 instruct. Also suggested memoryAlgorithmType:string variable on database.ts, to further add more memory types.  fixed minor punctuations, and changed summarize function of hypav2.ts(same as the one on supaMemory.ts)
This commit is contained in:
@@ -276,23 +276,31 @@
|
|||||||
<span class="text-textcolor mt-4">{language.type}</span>
|
<span class="text-textcolor mt-4">{language.type}</span>
|
||||||
|
|
||||||
<SelectInput value={
|
<SelectInput value={
|
||||||
$DataBase.supaMemoryType === 'hypaV2' ? 'hypaV2' :
|
$DataBase.hypav2 ? 'hypaV2' :
|
||||||
$DataBase.supaMemoryType !== 'none' ? 'supaMemory' :
|
$DataBase.supaModelType !== 'none' ? 'supaMemory' :
|
||||||
$DataBase.hanuraiEnable ? 'hanuraiMemory' : 'none'
|
$DataBase.hanuraiEnable ? 'hanuraiMemory' : 'none'
|
||||||
} on:change={(v) => {
|
} on:change={(v) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const value = v.target.value
|
const value = v.target.value
|
||||||
if (value === 'supaMemory'){
|
if (value === 'supaMemory'){
|
||||||
$DataBase.supaMemoryType = 'distilbart'
|
$DataBase.supaModelType = 'distilbart'
|
||||||
|
$DataBase.memoryAlgorithmType = 'supaMemory'
|
||||||
|
$DataBase.hypav2 = false
|
||||||
$DataBase.hanuraiEnable = false
|
$DataBase.hanuraiEnable = false
|
||||||
} else if (value === 'hanuraiMemory'){
|
} else if (value === 'hanuraiMemory'){
|
||||||
$DataBase.supaMemoryType = 'none'
|
$DataBase.supaModelType = 'none'
|
||||||
|
$DataBase.memoryAlgorithmType = 'hanuraiMemory'
|
||||||
|
$DataBase.hypav2 = false
|
||||||
$DataBase.hanuraiEnable = true
|
$DataBase.hanuraiEnable = true
|
||||||
} else if (value === 'hypaV2') {
|
} else if (value === 'hypaV2') {
|
||||||
$DataBase.supaMemoryType = 'hypaV2'
|
$DataBase.supaModelType = 'distilbart'
|
||||||
|
$DataBase.memoryAlgorithmType = 'hypaMemoryV2'
|
||||||
|
$DataBase.hypav2= true
|
||||||
$DataBase.hanuraiEnable = false
|
$DataBase.hanuraiEnable = false
|
||||||
} else {
|
} else {
|
||||||
$DataBase.supaMemoryType = 'none'
|
$DataBase.supaModelType = 'none'
|
||||||
|
$DataBase.memoryAlgorithmType = 'none'
|
||||||
|
$DataBase.hypav2 = false
|
||||||
$DataBase.hanuraiEnable = false
|
$DataBase.hanuraiEnable = false
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
@@ -309,27 +317,45 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<Check bind:check={$DataBase.hanuraiSplit} name="Text Spliting"/>
|
<Check bind:check={$DataBase.hanuraiSplit} name="Text Spliting"/>
|
||||||
</div>
|
</div>
|
||||||
{:else if $DataBase.supaMemoryType === 'hypaV2'}
|
{:else if $DataBase.hypav2}
|
||||||
<span class="mb-2 text-textcolor2 text-sm text-wrap break-words max-w-full">{language.hypaV2Desc}</span>
|
<span class="mb-2 text-textcolor2 text-sm text-wrap break-words max-w-full">{language.hypaV2Desc}</span>
|
||||||
|
<span class="text-textcolor mt-4">{language.SuperMemory} {language.model}</span>
|
||||||
|
<SelectInput className="mt-2 mb-2" bind:value={$DataBase.supaModelType}>
|
||||||
|
<OptionInput value="distilbart">distilbart-cnn-6-6 (Free/Local)</OptionInput>
|
||||||
|
<OptionInput value="instruct35">OpenAI 3.5 Turbo Instruct</OptionInput>
|
||||||
|
<OptionInput value="subModel">{language.submodel}</OptionInput>
|
||||||
|
</SelectInput>
|
||||||
|
{#if $DataBase.supaModelType === 'davinci' || $DataBase.supaModelType === 'curie' || $DataBase.supaModelType === 'instruct35'}
|
||||||
|
<span class="text-textcolor">{language.SuperMemory} OpenAI Key</span>
|
||||||
|
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryKey}/>
|
||||||
|
{/if}
|
||||||
|
<span class="text-textcolor">{language.SuperMemory} Prompt</span>
|
||||||
|
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryPrompt} placeholder="Leave it blank to use default"/>
|
||||||
|
<span class="text-textcolor">{language.HypaMemory} Model</span>
|
||||||
|
<SelectInput className="mt-2 mb-2" bind:value={$DataBase.hypaModel}>
|
||||||
|
<OptionInput value="MiniLM">MiniLM-L6-v2 (Free / Local)</OptionInput>
|
||||||
|
<OptionInput value="nomic">Nomic (Free / Local)</OptionInput>
|
||||||
|
<OptionInput value="ada">OpenAI Ada (Davinci / Curie Only)</OptionInput>
|
||||||
|
</SelectInput>
|
||||||
<span class="text-textcolor">{language.hypaChunkSize}</span>
|
<span class="text-textcolor">{language.hypaChunkSize}</span>
|
||||||
<NumberInput size="sm" marginBottom bind:value={$DataBase.hypaChunkSize} min={100} />
|
<NumberInput size="sm" marginBottom bind:value={$DataBase.hypaChunkSize} min={100} />
|
||||||
<span class="text-textcolor">{language.hypaAllocatedTokens}</span>
|
<span class="text-textcolor">{language.hypaAllocatedTokens}</span>
|
||||||
<NumberInput size="sm" marginBottom bind:value={$DataBase.hypaAllocatedTokens} min={100} />
|
<NumberInput size="sm" marginBottom bind:value={$DataBase.hypaAllocatedTokens} min={100} />
|
||||||
{:else if $DataBase.supaMemoryType !== 'none'}
|
{:else if ($DataBase.supaModelType !== 'none' && $DataBase.hypav2 === false)}
|
||||||
<span class="mb-2 text-textcolor2 text-sm text-wrap break-words max-w-full">{language.supaDesc}</span>
|
<span class="mb-2 text-textcolor2 text-sm text-wrap break-words max-w-full">{language.supaDesc}</span>
|
||||||
<span class="text-textcolor mt-4">{language.SuperMemory} {language.model}</span>
|
<span class="text-textcolor mt-4">{language.SuperMemory} {language.model}</span>
|
||||||
<SelectInput className="mt-2 mb-2" bind:value={$DataBase.supaMemoryType}>
|
<SelectInput className="mt-2 mb-2" bind:value={$DataBase.supaModelType}>
|
||||||
<OptionInput value="distilbart" >distilbart-cnn-6-6 (Free/Local)</OptionInput>
|
<OptionInput value="distilbart" >distilbart-cnn-6-6 (Free/Local)</OptionInput>
|
||||||
<OptionInput value="instruct35" >OpenAI 3.5 Turbo Instruct</OptionInput>
|
<OptionInput value="instruct35" >OpenAI 3.5 Turbo Instruct</OptionInput>
|
||||||
<OptionInput value="subModel" >{language.submodel}</OptionInput>
|
<OptionInput value="subModel" >{language.submodel}</OptionInput>
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
<span class="text-textcolor">{language.maxSupaChunkSize}</span>
|
<span class="text-textcolor">{language.maxSupaChunkSize}</span>
|
||||||
<NumberInput size="sm" marginBottom bind:value={$DataBase.maxSupaChunkSize} min={100} />
|
<NumberInput size="sm" marginBottom bind:value={$DataBase.maxSupaChunkSize} min={100} />
|
||||||
{#if $DataBase.supaMemoryType === 'davinci' || $DataBase.supaMemoryType === 'curie' || $DataBase.supaMemoryType === 'instruct35'}
|
{#if $DataBase.supaModelType === 'davinci' || $DataBase.supaModelType === 'curie' || $DataBase.supaModelType === 'instruct35'}
|
||||||
<span class="text-textcolor">{language.SuperMemory} OpenAI Key</span>
|
<span class="text-textcolor">{language.SuperMemory} OpenAI Key</span>
|
||||||
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryKey}/>
|
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryKey}/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $DataBase.supaMemoryType !== 'none'}
|
{#if $DataBase.supaModelType !== 'none'}
|
||||||
<span class="text-textcolor">{language.SuperMemory} Prompt</span>
|
<span class="text-textcolor">{language.SuperMemory} Prompt</span>
|
||||||
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryPrompt} placeholder="Leave it blank to use default"/>
|
<TextInput size="sm" marginBottom bind:value={$DataBase.supaMemoryPrompt} placeholder="Leave it blank to use default"/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
|
||||||
{#if $DataBase.supaMemoryType !== 'none' || $DataBase.hanuraiEnable}
|
{#if $DataBase.supaModelType !== 'none' || $DataBase.hanuraiEnable}
|
||||||
{#if $DataBase.hanuraiEnable}
|
{#if $DataBase.hanuraiEnable}
|
||||||
<div class="flex mt-2 items-center">
|
<div class="flex mt-2 items-center">
|
||||||
<Check bind:check={currentChar.data.supaMemory} name={ language.hanuraiMemory}/>
|
<Check bind:check={currentChar.data.supaMemory} name={ language.hanuraiMemory}/>
|
||||||
@@ -904,7 +904,7 @@
|
|||||||
<span> <Help key="utilityBot" name={language.utilityBot}/></span>
|
<span> <Help key="utilityBot" name={language.utilityBot}/></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $DataBase.supaMemoryType === 'hypaV2'}
|
{#if $DataBase.supaModelType !== 'none' && $DataBase.hypav2}
|
||||||
<Button
|
<Button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
currentChar.data.chats[currentChar.data.chatPage].hypaV2Data ??= {
|
currentChar.data.chats[currentChar.data.chatPage].hypaV2Data ??= {
|
||||||
|
|||||||
@@ -165,7 +165,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if $DataBase.supaMemoryType !== 'none' || $DataBase.hanuraiEnable}
|
{#if $DataBase.supaModelType !== 'none' || $DataBase.hanuraiEnable}
|
||||||
{#if $DataBase.hanuraiEnable}
|
{#if $DataBase.hanuraiEnable}
|
||||||
<div class="flex mt-2 items-center">
|
<div class="flex mt-2 items-center">
|
||||||
<CheckInput bind:check={chara.supaMemory} name={ language.hanuraiMemory}/>
|
<CheckInput bind:check={chara.supaMemory} name={ language.hanuraiMemory}/>
|
||||||
|
|||||||
@@ -714,7 +714,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
|
|||||||
currentTokens += await tokenizer.tokenizeChat(chat)
|
currentTokens += await tokenizer.tokenizeChat(chat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nowChatroom.supaMemory && (db.supaMemoryType !== 'none' || db.hanuraiEnable)){
|
if(nowChatroom.supaMemory && (db.supaModelType !== 'none' || db.hanuraiEnable || db.hypav2)){
|
||||||
chatProcessStage.set(2)
|
chatProcessStage.set(2)
|
||||||
if(db.hanuraiEnable){
|
if(db.hanuraiEnable){
|
||||||
const hn = await hanuraiMemory(chats, {
|
const hn = await hanuraiMemory(chats, {
|
||||||
@@ -730,9 +730,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
|
|||||||
chats = hn.chats
|
chats = hn.chats
|
||||||
currentTokens = hn.tokens
|
currentTokens = hn.tokens
|
||||||
}
|
}
|
||||||
else if(db.supaMemoryType === 'hypaV2'){
|
else if(db.hypav2){ //HypaV2 support needs to be changed like this.
|
||||||
const sp = await hypaMemoryV2(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer)
|
const sp = await hypaMemoryV2(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer)
|
||||||
|
console.log("All chats: ", chats)
|
||||||
if(sp.error){
|
if(sp.error){
|
||||||
|
console.log(sp)
|
||||||
alertError(sp.error)
|
alertError(sp.error)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,209 +4,306 @@ import type { ChatTokenizer } from "src/ts/tokenizer";
|
|||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { requestChatData } from "../request";
|
import { requestChatData } from "../request";
|
||||||
import { HypaProcesser } from "./hypamemory";
|
import { HypaProcesser } from "./hypamemory";
|
||||||
|
import { globalFetch } from "src/ts/storage/globalApi";
|
||||||
|
import { runSummarizer } from "../transformers";
|
||||||
|
import { last, remove } from "lodash";
|
||||||
|
|
||||||
export interface HypaV2Data{
|
export interface HypaV2Data {
|
||||||
chunks: {
|
chunks: {
|
||||||
text:string
|
text: string;
|
||||||
targetId:string
|
targetId: string;
|
||||||
}[]
|
}[];
|
||||||
mainChunks: {
|
mainChunks: {
|
||||||
text:string
|
text: string;
|
||||||
targetId:string
|
targetId: string;
|
||||||
}[]
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function summary(stringlizedChat: string): Promise<{ success: boolean; data: string }> {
|
||||||
|
const db = get(DataBase);
|
||||||
|
console.log("Summarizing");
|
||||||
|
|
||||||
async function summary(stringlizedChat:string):Promise<{
|
if (db.supaModelType === 'distilbart') {
|
||||||
success:boolean
|
try {
|
||||||
data:string
|
const sum = await runSummarizer(stringlizedChat);
|
||||||
}>{
|
return { success: true, data: sum };
|
||||||
const promptbody:OpenAIChat[] = [
|
} catch (error) {
|
||||||
{
|
return {
|
||||||
role: "user",
|
success: false,
|
||||||
content: stringlizedChat
|
data: "SupaMemory: Summarizer: " + `${error}`
|
||||||
},
|
};
|
||||||
{
|
|
||||||
role: "system",
|
|
||||||
content: "Summarize this roleplay scene in a coherent narrative format for future reference. Summarize what happened, focusing on events and interactions between them. If someone or something is new or changed, include a brief characterization of them."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const da = await requestChatData({
|
|
||||||
formated: promptbody,
|
|
||||||
bias: {},
|
|
||||||
useStreaming: false,
|
|
||||||
noMultiGen: true
|
|
||||||
}, 'model')
|
|
||||||
if(da.type === 'fail' || da.type === 'streaming' || da.type === 'multiline'){
|
|
||||||
return {
|
|
||||||
data: "Hypamemory HTTP: " + da.result,
|
|
||||||
success: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
data: da.result,
|
const supaPrompt = db.supaMemoryPrompt === '' ?
|
||||||
success: true
|
"[Summarize the ongoing role story, It must also remove redundancy and unnecessary text and content from the output to reduce tokens for gpt3 and other sublanguage models]\n"
|
||||||
|
: db.supaMemoryPrompt;
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if (db.supaModelType !== 'subModel') {
|
||||||
|
const promptbody = stringlizedChat + '\n\n' + supaPrompt + "\n\nOutput:";
|
||||||
|
|
||||||
|
const da = await globalFetch("https://api.openai.com/v1/completions", {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer " + db.supaMemoryKey
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
"model": db.supaModelType === 'curie' ? "text-curie-001"
|
||||||
|
: db.supaModelType === 'instruct35' ? 'gpt-3.5-turbo-instruct'
|
||||||
|
: "text-davinci-003",
|
||||||
|
"prompt": promptbody,
|
||||||
|
"max_tokens": 600,
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log("Using openAI instruct 3.5 for SupaMemory");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!da.ok) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: "SupaMemory: HTTP: " + JSON.stringify(da)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (await da.data)?.choices[0]?.text?.trim();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: "SupaMemory: HTTP: " + JSON.stringify(da)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, data: result };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: "SupaMemory: HTTP: " + error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const promptbody: OpenAIChat[] = [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: stringlizedChat
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "system",
|
||||||
|
content: supaPrompt
|
||||||
|
}
|
||||||
|
];
|
||||||
|
console.log("Using submodel: ", db.subModel, "for supaMemory model");
|
||||||
|
const da = await requestChatData({
|
||||||
|
formated: promptbody,
|
||||||
|
bias: {},
|
||||||
|
useStreaming: false,
|
||||||
|
noMultiGen: true
|
||||||
|
}, 'submodel');
|
||||||
|
if (da.type === 'fail' || da.type === 'streaming' || da.type === 'multiline') {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: "SupaMemory: HTTP: " + da.result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
result = da.result;
|
||||||
}
|
}
|
||||||
|
return { success: true, data: result };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function hypaMemoryV2(
|
export async function hypaMemoryV2(
|
||||||
chats:OpenAIChat[],
|
chats: OpenAIChat[],
|
||||||
currentTokens:number,
|
currentTokens: number,
|
||||||
maxContextTokens:number,
|
maxContextTokens: number,
|
||||||
room:Chat,
|
room: Chat,
|
||||||
char:character|groupChat,
|
char: character | groupChat,
|
||||||
tokenizer:ChatTokenizer,
|
tokenizer: ChatTokenizer,
|
||||||
arg:{asHyper?:boolean} = {}
|
arg: { asHyper?: boolean, summaryModel?: string, summaryPrompt?: string, hypaModel?: string } = {}
|
||||||
): Promise<{ currentTokens: number; chats: OpenAIChat[]; error?:string; memory?:HypaV2Data;}>{
|
): Promise<{ currentTokens: number; chats: OpenAIChat[]; error?: string; memory?: HypaV2Data; }> {
|
||||||
|
|
||||||
const db = get(DataBase)
|
const db = get(DataBase);
|
||||||
|
const data: HypaV2Data = room.hypaV2Data ?? { chunks: [], mainChunks: [] };
|
||||||
|
|
||||||
const data:HypaV2Data = room.hypaV2Data ?? {
|
let allocatedTokens = db.hypaAllocatedTokens;
|
||||||
chunks:[],
|
let chunkSize = db.hypaChunkSize;
|
||||||
mainChunks:[]
|
currentTokens += allocatedTokens + 50;
|
||||||
}
|
let mainPrompt = "";
|
||||||
|
const lastTwoChats = chats.slice(-2);
|
||||||
//this is for the prompt
|
// Error handling for infinite summarization attempts
|
||||||
|
let summarizationFailures = 0;
|
||||||
|
const maxSummarizationFailures = 3;
|
||||||
|
let lastMainChunkTargetId = '';
|
||||||
|
|
||||||
let allocatedTokens = db.hypaAllocatedTokens
|
// Ensure correct targetId matching
|
||||||
let chunkSize = db.hypaChunkSize
|
const getValidChatIndex = (targetId: string) => {
|
||||||
currentTokens += allocatedTokens
|
return chats.findIndex(chat => chat.memo === targetId);
|
||||||
currentTokens += 50 //this is for the template prompt
|
};
|
||||||
let mainPrompt = ""
|
|
||||||
|
|
||||||
while(data.mainChunks.length > 0){
|
// Processing mainChunks
|
||||||
const chunk = data.mainChunks[0]
|
if (data.mainChunks.length > 0) {
|
||||||
const ind = chats.findIndex(e => e.memo === chunk.targetId)
|
const chunk = data.mainChunks[0];
|
||||||
if(ind === -1){
|
const ind = getValidChatIndex(chunk.targetId);
|
||||||
data.mainChunks.shift()
|
if (ind !== -1) {
|
||||||
continue
|
const removedChats = chats.splice(0, ind + 1);
|
||||||
|
console.log("removed chats", removedChats);
|
||||||
|
for (const chat of removedChats) {
|
||||||
|
currentTokens -= await tokenizer.tokenizeChat(chat);
|
||||||
|
}
|
||||||
|
mainPrompt = chunk.text;
|
||||||
|
const mpToken = await tokenizer.tokenizeChat({ role: 'system', content: mainPrompt });
|
||||||
|
allocatedTokens -= mpToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removedChats = chats.splice(0, ind)
|
|
||||||
for(const chat of removedChats){
|
|
||||||
currentTokens -= await tokenizer.tokenizeChat(chat)
|
|
||||||
}
|
|
||||||
chats = chats.slice(ind)
|
|
||||||
mainPrompt = chunk.text
|
|
||||||
const mpToken = await tokenizer.tokenizeChat({role:'system', content:mainPrompt})
|
|
||||||
allocatedTokens -= mpToken
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while(currentTokens >= maxContextTokens){
|
// Token management loop
|
||||||
|
while (currentTokens >= maxContextTokens) {
|
||||||
let idx = 0
|
let idx = 0;
|
||||||
let targetId = ''
|
let targetId = '';
|
||||||
const halfData:OpenAIChat[] = []
|
const halfData: OpenAIChat[] = [];
|
||||||
|
|
||||||
let halfDataTokens = 0
|
let halfDataTokens = 0;
|
||||||
while(halfDataTokens < chunkSize){
|
while (halfDataTokens < chunkSize && (idx <= chats.length - 4)) { // Ensure latest two chats are not added to summarization.
|
||||||
const chat = chats[idx]
|
const chat = chats[idx];
|
||||||
if(!chat){
|
halfDataTokens += await tokenizer.tokenizeChat(chat);
|
||||||
break
|
halfData.push(chat);
|
||||||
}
|
idx++;
|
||||||
halfDataTokens += await tokenizer.tokenizeChat(chat)
|
targetId = chat.memo;
|
||||||
halfData.push(chat)
|
console.log("current target chat: ", chat);
|
||||||
idx++
|
|
||||||
targetId = chat.memo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringlizedChat = halfData.map(e => `${e.role}: ${e.content}`).join('\n')
|
// Avoid summarizing the last two chats
|
||||||
|
if (halfData.length < 3) break;
|
||||||
|
|
||||||
const summaryData = await summary(stringlizedChat)
|
const stringlizedChat = halfData.map(e => `${e.role}: ${e.content}`).join('\n');
|
||||||
|
const summaryData = await summary(stringlizedChat);
|
||||||
|
|
||||||
if(!summaryData.success){
|
if (!summaryData.success) {
|
||||||
return {
|
summarizationFailures++;
|
||||||
currentTokens: currentTokens,
|
if (summarizationFailures >= maxSummarizationFailures) {
|
||||||
chats: chats,
|
return {
|
||||||
error: summaryData.data
|
currentTokens: currentTokens,
|
||||||
|
chats: chats,
|
||||||
|
error: "Summarization failed multiple times. Aborting to prevent infinite loop."
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const summaryDataToken = await tokenizer.tokenizeChat({role:'system', content:summaryData.data})
|
summarizationFailures = 0; // Reset failure counter on success
|
||||||
mainPrompt += `\n\n${summaryData.data}`
|
|
||||||
currentTokens -= halfDataTokens
|
const summaryDataToken = await tokenizer.tokenizeChat({ role: 'system', content: summaryData.data });
|
||||||
allocatedTokens -= summaryDataToken
|
mainPrompt += `\n\n${summaryData.data}`;
|
||||||
|
currentTokens -= halfDataTokens;
|
||||||
|
allocatedTokens -= summaryDataToken;
|
||||||
|
|
||||||
data.mainChunks.unshift({
|
data.mainChunks.unshift({
|
||||||
text: mainPrompt,
|
text: summaryData.data,
|
||||||
targetId: targetId
|
targetId: targetId
|
||||||
})
|
});
|
||||||
|
|
||||||
if(allocatedTokens < 1500){
|
// Split the summary into chunks based on double line breaks
|
||||||
const summarizedMp = await summary(mainPrompt)
|
const splitted = summaryData.data.split('\n\n').map(e => e.trim()).filter(e => e.length > 0);
|
||||||
const mpToken = await tokenizer.tokenizeChat({role:'system', content:mainPrompt})
|
|
||||||
const summaryToken = await tokenizer.tokenizeChat({role:'system', content:summarizedMp.data})
|
|
||||||
|
|
||||||
allocatedTokens -= summaryToken
|
// Update chunks with the new summary
|
||||||
allocatedTokens += mpToken
|
data.chunks.push(...splitted.map(e => ({
|
||||||
|
text: e,
|
||||||
|
targetId: targetId
|
||||||
|
})));
|
||||||
|
|
||||||
const splited = mainPrompt.split('\n\n').map(e => e.trim()).filter(e => e.length > 0)
|
// Remove summarized chats
|
||||||
|
chats.splice(0, idx);
|
||||||
data.chunks.push(...splited.map(e => ({
|
|
||||||
text: e,
|
|
||||||
targetId: targetId
|
|
||||||
})))
|
|
||||||
|
|
||||||
data.mainChunks[0].text = mainPrompt
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const processer = new HypaProcesser("nomic")
|
|
||||||
|
|
||||||
await processer.addText(data.chunks.filter(v => {
|
// Construct the mainPrompt from mainChunks until half of the allocatedTokens are used
|
||||||
return v.text.trim().length > 0
|
mainPrompt = "";
|
||||||
}).map((v) => {
|
let mainPromptTokens = 0;
|
||||||
return "search_document: " + v.text.trim()
|
for (const chunk of data.mainChunks) {
|
||||||
}))
|
const chunkTokens = await tokenizer.tokenizeChat({ role: 'system', content: chunk.text });
|
||||||
|
if (mainPromptTokens + chunkTokens > allocatedTokens / 2) break;
|
||||||
|
mainPrompt += `\n\n${chunk.text}`;
|
||||||
|
mainPromptTokens += chunkTokens;
|
||||||
|
lastMainChunkTargetId = chunk.targetId;
|
||||||
|
}
|
||||||
|
|
||||||
let scoredResults:{[key:string]:number} = {}
|
// Fetch additional memory from chunks
|
||||||
for(let i=0;i<3;i++){
|
const processor = new HypaProcesser(db.hypaModel);
|
||||||
const pop = chats[chats.length - i - 1]
|
processor.oaikey = db.supaMemoryKey;
|
||||||
if(!pop){
|
|
||||||
break
|
// Find the smallest index of chunks with the same targetId as lastMainChunkTargetId
|
||||||
|
const lastMainChunkIndex = data.chunks.reduce((minIndex, chunk, index) => {
|
||||||
|
if (chunk.targetId === lastMainChunkTargetId) {
|
||||||
|
return Math.min(minIndex, index);
|
||||||
}
|
}
|
||||||
const searched = await processer.similaritySearchScored(`search_query: ${pop.content}`)
|
return minIndex;
|
||||||
for(const result of searched){
|
}, data.chunks.length);
|
||||||
const score = result[1]/(i+1)
|
|
||||||
if(scoredResults[result[0]]){
|
// Filter chunks to only include those older than the last mainChunk's targetId
|
||||||
scoredResults[result[0]] += score
|
const olderChunks = lastMainChunkIndex !== data.chunks.length
|
||||||
}else{
|
? data.chunks.slice(0, lastMainChunkIndex)
|
||||||
scoredResults[result[0]] = score
|
: data.chunks;
|
||||||
}
|
|
||||||
|
console.log("Older Chunks:", olderChunks);
|
||||||
|
|
||||||
|
// Add older chunks to processor for similarity search
|
||||||
|
await processor.addText(olderChunks.filter(v => v.text.trim().length > 0).map(v => "search_document: " + v.text.trim()));
|
||||||
|
|
||||||
|
let scoredResults: { [key: string]: number } = {};
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const pop = chats[chats.length - i - 1];
|
||||||
|
if (!pop) break;
|
||||||
|
const searched = await processor.similaritySearchScored(`search_query: ${pop.content}`);
|
||||||
|
for (const result of searched) {
|
||||||
|
const score = result[1] / (i + 1);
|
||||||
|
scoredResults[result[0]] = (scoredResults[result[0]] || 0) + score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scoredArray = Object.entries(scoredResults).sort((a,b) => b[1] - a[1])
|
const scoredArray = Object.entries(scoredResults).sort((a, b) => b[1] - a[1]);
|
||||||
|
let chunkResultPrompts = "";
|
||||||
let chunkResultPrompts = ""
|
let chunkResultTokens = 0;
|
||||||
while(allocatedTokens > 0){
|
while (allocatedTokens - mainPromptTokens - chunkResultTokens > 0 && scoredArray.length > 0) {
|
||||||
const target = scoredArray.shift()
|
const [text] = scoredArray.shift();
|
||||||
if(!target){
|
const tokenized = await tokenizer.tokenizeChat({ role: 'system', content: text.substring(14) });
|
||||||
break
|
if (tokenized > allocatedTokens - mainPromptTokens - chunkResultTokens) break;
|
||||||
}
|
chunkResultPrompts += text.substring(14) + '\n\n';
|
||||||
const tokenized = await tokenizer.tokenizeChat({
|
chunkResultTokens += tokenized;
|
||||||
role: 'system',
|
|
||||||
content: target[0].substring(14)
|
|
||||||
})
|
|
||||||
if(tokenized > allocatedTokens){
|
|
||||||
break
|
|
||||||
}
|
|
||||||
chunkResultPrompts += target[0].substring(14) + '\n\n'
|
|
||||||
allocatedTokens -= tokenized
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fullResult = `<Past Events Summary>${mainPrompt}</Past Events Summary>\n<Past Events Details>${chunkResultPrompts}</Past Events Details>`;
|
||||||
const fullResult = `<Past Events Summary>${mainPrompt}</Past Events Summary>\n<Past Events Details>${chunkResultPrompts}</Past Events Details>`
|
|
||||||
|
|
||||||
chats.unshift({
|
chats.unshift({
|
||||||
role: "system",
|
role: "system",
|
||||||
content: fullResult,
|
content: fullResult,
|
||||||
memo: "supaMemory"
|
memo: "supaMemory"
|
||||||
})
|
});
|
||||||
|
|
||||||
|
// Add the remaining chats after the last mainChunk's targetId
|
||||||
|
const lastTargetId = data.mainChunks.length > 0 ? data.mainChunks[0].targetId : null;
|
||||||
|
if (lastTargetId) {
|
||||||
|
const lastIndex = getValidChatIndex(lastTargetId);
|
||||||
|
if (lastIndex !== -1) {
|
||||||
|
const remainingChats = chats.slice(lastIndex + 1);
|
||||||
|
chats = [chats[0], ...remainingChats];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add last two chats if they exist and are not duplicates
|
||||||
|
if (lastTwoChats.length === 2) {
|
||||||
|
const [lastChat1, lastChat2] = lastTwoChats;
|
||||||
|
if (!chats.some(chat => chat.memo === lastChat1.memo)) {
|
||||||
|
chats.push(lastChat1);
|
||||||
|
}
|
||||||
|
if (!chats.some(chat => chat.memo === lastChat2.memo)) {
|
||||||
|
chats.push(lastChat2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("model being used: ", db.hypaModel, db.supaModelType, "\nCurrent session tokens: ", currentTokens, "\nAll chats, including memory system prompt: ", chats, "\nMemory data, with all the chunks: ", data);
|
||||||
return {
|
return {
|
||||||
currentTokens: currentTokens,
|
currentTokens: currentTokens,
|
||||||
chats: chats,
|
chats: chats,
|
||||||
memory: data
|
memory: data
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export async function supaMemory(
|
|||||||
|
|
||||||
async function summarize(stringlizedChat:string){
|
async function summarize(stringlizedChat:string){
|
||||||
|
|
||||||
if(db.supaMemoryType === 'distilbart'){
|
if(db.supaModelType === 'distilbart'){
|
||||||
try {
|
try {
|
||||||
const sum = await runSummarizer(stringlizedChat)
|
const sum = await runSummarizer(stringlizedChat)
|
||||||
return sum
|
return sum
|
||||||
@@ -204,7 +204,7 @@ export async function supaMemory(
|
|||||||
|
|
||||||
let result = ''
|
let result = ''
|
||||||
|
|
||||||
if(db.supaMemoryType !== 'subModel'){
|
if(db.supaModelType !== 'subModel'){
|
||||||
const promptbody = stringlizedChat + '\n\n' + supaPrompt + "\n\nOutput:"
|
const promptbody = stringlizedChat + '\n\n' + supaPrompt + "\n\nOutput:"
|
||||||
|
|
||||||
const da = await globalFetch("https://api.openai.com/v1/completions",{
|
const da = await globalFetch("https://api.openai.com/v1/completions",{
|
||||||
@@ -214,8 +214,8 @@ export async function supaMemory(
|
|||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
"model": db.supaMemoryType === 'curie' ? "text-curie-001"
|
"model": db.supaModelType === 'curie' ? "text-curie-001"
|
||||||
: db.supaMemoryType === 'instruct35' ? 'gpt-3.5-turbo-instruct'
|
: db.supaModelType === 'instruct35' ? 'gpt-3.5-turbo-instruct'
|
||||||
: "text-davinci-003",
|
: "text-davinci-003",
|
||||||
"prompt": promptbody,
|
"prompt": promptbody,
|
||||||
"max_tokens": 600,
|
"max_tokens": 600,
|
||||||
|
|||||||
@@ -230,8 +230,8 @@ export function setDatabase(data:Database){
|
|||||||
if(checkNullish(data.supaMemoryKey)){
|
if(checkNullish(data.supaMemoryKey)){
|
||||||
data.supaMemoryKey = ""
|
data.supaMemoryKey = ""
|
||||||
}
|
}
|
||||||
if(checkNullish(data.supaMemoryType)){
|
if(checkNullish(data.supaModelType)){
|
||||||
data.supaMemoryType = "none"
|
data.supaModelType = "none"
|
||||||
}
|
}
|
||||||
if(checkNullish(data.askRemoval)){
|
if(checkNullish(data.askRemoval)){
|
||||||
data.askRemoval = true
|
data.askRemoval = true
|
||||||
@@ -527,7 +527,7 @@ export interface Database{
|
|||||||
useStreaming:boolean
|
useStreaming:boolean
|
||||||
palmAPI:string,
|
palmAPI:string,
|
||||||
supaMemoryKey:string
|
supaMemoryKey:string
|
||||||
supaMemoryType:string
|
supaModelType:string
|
||||||
textScreenColor?:string
|
textScreenColor?:string
|
||||||
textBorder?:boolean
|
textBorder?:boolean
|
||||||
textScreenRounded?:boolean
|
textScreenRounded?:boolean
|
||||||
@@ -569,6 +569,8 @@ export interface Database{
|
|||||||
useAdditionalAssetsPreview:boolean,
|
useAdditionalAssetsPreview:boolean,
|
||||||
usePlainFetch:boolean
|
usePlainFetch:boolean
|
||||||
hypaMemory:boolean
|
hypaMemory:boolean
|
||||||
|
hypav2:boolean
|
||||||
|
memoryAlgorithmType:string // To enable new memory module/algorithms
|
||||||
proxyRequestModel:string
|
proxyRequestModel:string
|
||||||
ooba:OobaSettings
|
ooba:OobaSettings
|
||||||
ainconfig: AINsettings
|
ainconfig: AINsettings
|
||||||
|
|||||||
Reference in New Issue
Block a user