+
-
HypaV3 Data
+
HypaV3 Data
{
- let confirmed = await alertConfirm(
- "This action cannot be undone. Do you want to reset HypaV3 data?"
- );
-
- if (confirmed) {
- confirmed = await alertConfirm(
+ if (
+ await alertConfirmTwice(
+ "This action cannot be undone. Do you want to reset HypaV3 data?",
"This action is irreversible. Do you really, really want to reset HypaV3 data?"
- );
-
- if (confirmed) {
- DBState.db.characters[$selectedCharID].chats[
- DBState.db.characters[$selectedCharID].chatPage
- ].hypaV3Data = {
- summaries: [],
- };
- }
+ )
+ ) {
+ DBState.db.characters[$selectedCharID].chats[
+ DBState.db.characters[$selectedCharID].chatPage
+ ].hypaV3Data = {
+ summaries: [],
+ lastSelectedSummaries: [],
+ };
+ } else {
+ showHypaV3Alert();
}
}}
>
+
{
alertStore.set({
type: "none",
@@ -67,168 +390,233 @@
-
- {#each DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].hypaV3Data.summaries as summary, i}
-
-
-
+
+
+
+ {#if hypaV3DataState.summaries.length === 0}
+
No summaries yet
+ {/if}
+
+ {#each hypaV3DataState.summaries as summary, i}
+ {#if summaryUIStates[i]}
+
+
Summary #{i + 1}
+
+ toggleTranslate(i, false),
+ onAlternativeAction: () => toggleTranslate(i, true),
+ }}
+ >
+
+
+
{
summary.isImportant = !summary.isImportant;
}}
>
-
+
+
{
- hypaV3IsResummarizing = true;
-
- try {
- const char = DBState.db.characters[$selectedCharID];
- const chat =
- char.chats[
- DBState.db.characters[$selectedCharID].chatPage
- ];
- const firstMessage =
- chat.fmIndex === -1
- ? char.firstMessage
- : char.alternateGreetings?.[chat.fmIndex ?? 0];
- const toSummarize = summary.chatMemos.map(
- (chatMemo) => {
- if (chatMemo == null) {
- return {
- role: "assistant",
- data: firstMessage,
- };
- }
-
- const msg = chat.message.find(
- (m) => m.chatId === chatMemo
- );
- return msg
- ? {
- role:
- msg.role === "char"
- ? "assistant"
- : msg.role,
- data: msg.data,
- }
- : null;
- }
- );
- const stringifiedChats = toSummarize
- .map((m) => `${m.role}: ${m.data}`)
- .join("\n");
- const summarizeResult =
- await summarize(stringifiedChats);
-
- if (summarizeResult.success) {
- summary.text = summarizeResult.data;
- }
- } finally {
- hypaV3IsResummarizing = false;
- }
- }}
- disabled={hypaV3IsResummarizing}
+ class="p-2 text-zinc-400 hover:text-zinc-200 transition-colors"
+ onclick={async () => await toggleReroll(i)}
+ disabled={!isRerollable(i)}
>
-
-
-
-
-
-
- Connected Messages ({summary.chatMemos.length})
-
-
-
+
+
+
+
+ {#if summaryUIStates[i].translation}
+
+
Translation
+
+ {summaryUIStates[i].translation}
+
+
+ {/if}
+
+
+ {#if summaryUIStates[i].rerolledText}
+
+
+
Rerolled Summary
+
+
+ toggleTranslateRerolled(i, false),
+ onAlternativeAction: () =>
+ toggleTranslateRerolled(i, true),
+ }}
+ >
+
+
+
+
+ {
+ summaryUIStates[i].rerolledText = null;
+ summaryUIStates[i].rerolledTranslation = null;
+ }}
+ >
+
+
+
+
+ {
+ summary.text = summaryUIStates[i].rerolledText!;
+ summaryUIStates[i].translation = null;
+ summaryUIStates[i].rerolledText = null;
+ summaryUIStates[i].rerolledTranslation = null;
+ }}
+ >
+
+
+
+
+
+
+
+ {#if summaryUIStates[i].rerolledTranslation}
+
+
Rerolled Translation
+
+ {summaryUIStates[i].rerolledTranslation}
+
+
+ {/if}
+
+ {/if}
+
+
+
+
+
+ Connected Messages ({summary.chatMemos.length})
+
+
+ toggleTranslateExpandedMessage(false),
+ onAlternativeAction: () =>
+ toggleTranslateExpandedMessage(true),
+ }}
+ >
+
+
+
+
+
{#each summary.chatMemos as chatMemo}
{
- hypaV3ExpandedChatMemo =
- hypaV3ExpandedChatMemo.summaryChatMemos ===
- summary.chatMemos &&
- hypaV3ExpandedChatMemo.summaryChatMemo === chatMemo
- ? { summaryChatMemos: [], summaryChatMemo: "" }
- : {
- summaryChatMemos: summary.chatMemos,
- summaryChatMemo: chatMemo,
- };
- }}
+ onclick={() => toggleExpandMessage(i, chatMemo)}
>
{chatMemo == null ? "First Message" : chatMemo}
{/each}
-
- {#if hypaV3ExpandedChatMemo.summaryChatMemos === summary.chatMemos && hypaV3ExpandedChatMemo.summaryChatMemo !== ""}
-
-
- {(() => {
- const char = DBState.db.characters[$selectedCharID];
- const chat =
- char.chats[
- DBState.db.characters[$selectedCharID].chatPage
- ];
- const firstMessage =
- chat.fmIndex === -1
- ? char.firstMessage
- : char.alternateGreetings?.[chat.fmIndex ?? 0];
- const targetMessage =
- hypaV3ExpandedChatMemo.summaryChatMemo == null
- ? { role: "char", data: firstMessage }
- : chat.message.find(
- (m) =>
- m.chatId ===
- hypaV3ExpandedChatMemo.summaryChatMemo
- );
+
+ {#if expandedMessageUIState?.summaryIndex === i}
+ {@const message = getMessageFromChatMemo(
+ expandedMessageUIState.selectedChatMemo
+ )}
+
+ {#if message}
+
+
+ {message.role}'s Message
+
+
+
+ {message.data}
+
+ {:else}
+
Message not found
+ {/if}
- if (targetMessage) {
- const displayRole =
- targetMessage.role === "char"
- ? char.name
- : targetMessage.role;
- return `${displayRole}:\n${targetMessage.data}`;
- }
-
- return "Message not found";
- })()}
-
+
+ {#if expandedMessageUIState.translation}
+
+
Translation
+
+ {expandedMessageUIState.translation}
+
+
+ {/if}
{/if}
-
+ {/if}
{/each}
- {#if DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].hypaV3Data.summaries.length === 0}
-
No summaries yet
+
+ {#if true}
+
+ {@const nextMessage = getNextMessageToSummarize()}
+ {#if nextMessage}
+
+
+ HypaV3 will summarize [{nextMessage.chatId}]
+
+
+ {nextMessage.data}
+
+
+ {/if}
{/if}
diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte
index 086e234a..8259f29b 100644
--- a/src/lib/Setting/Pages/OtherBotSettings.svelte
+++ b/src/lib/Setting/Pages/OtherBotSettings.svelte
@@ -15,8 +15,9 @@
import { getCharImage } from "src/ts/characters";
import Arcodion from "src/lib/UI/Arcodion.svelte";
import CheckInput from "src/lib/UI/GUI/CheckInput.svelte";
- import TextAreaInput from "src/lib/UI/GUI/TextAreaInput.svelte";
- import { hypaMemoryV3 } from "src/ts/process/memory/hypav3";
+ import TextAreaInput from "src/lib/UI/GUI/TextAreaInput.svelte";
+ import { untrack } from "svelte";
+
$effect.pre(() => {
DBState.db.NAIImgConfig ??= {
width: 512,
@@ -40,6 +41,32 @@
});
let submenu = $state(DBState.db.useLegacyGUI ? -1 : 0)
+
+ // HypaV3
+ $effect(() => {
+ const newValue = Math.min(DBState.db.hypaV3Settings.recentMemoryRatio, 1);
+
+ untrack(() => {
+ DBState.db.hypaV3Settings.recentMemoryRatio = newValue;
+
+ if (newValue + DBState.db.hypaV3Settings.similarMemoryRatio > 1) {
+ DBState.db.hypaV3Settings.similarMemoryRatio = 1 - newValue;
+ }
+ })
+ })
+
+ $effect(() => {
+ const newValue = Math.min(DBState.db.hypaV3Settings.similarMemoryRatio, 1);
+
+ untrack(() => {
+ DBState.db.hypaV3Settings.similarMemoryRatio = newValue;
+
+ if (newValue + DBState.db.hypaV3Settings.recentMemoryRatio > 1) {
+ DBState.db.hypaV3Settings.recentMemoryRatio = 1 - newValue;
+ }
+ })
+ });
+ // End HypaV3
{language.otherBots}
@@ -467,31 +494,31 @@
OpenAI 3.5 Turbo Instruct
{language.submodel}
- {#if DBState.db.supaModelType === 'instruct35'}
+ {#if DBState.db.supaModelType === "instruct35"}
OpenAI API Key
-
+
{/if}
{language.summarizationPrompt}
-
+
Memory Tokens Ratio
-
+
Extra Summarization Ratio
-
+
Max Chats Per Summary
-
+
Recent Memory Ratio
-
+
Similar Memory Ratio
-
+
Random Memory Ratio
-
+
-
+
-
+
{:else if (DBState.db.supaModelType !== 'none' && DBState.db.hypav2 === false && DBState.db.hypaV3 === false)}
{language.supaDesc}
diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte
index fccb78db..93a4c98a 100644
--- a/src/lib/SideBars/CharConfig.svelte
+++ b/src/lib/SideBars/CharConfig.svelte
@@ -1113,6 +1113,7 @@
onclick={() => {
DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].hypaV3Data ??= {
summaries: [],
+ lastSelectedSummaries: [],
}
showHypaV3Alert()
}}
diff --git a/src/ts/process/memory/hypav3.ts b/src/ts/process/memory/hypav3.ts
index 4786c1bf..91bef8d6 100644
--- a/src/ts/process/memory/hypav3.ts
+++ b/src/ts/process/memory/hypav3.ts
@@ -24,6 +24,7 @@ interface Summary {
interface HypaV3Data {
summaries: Summary[];
+ lastSelectedSummaries?: number[];
}
export interface SerializableHypaV3Data {
@@ -32,6 +33,7 @@ export interface SerializableHypaV3Data {
chatMemos: string[];
isImportant: boolean;
}[];
+ lastSelectedSummaries?: number[];
}
interface SummaryChunk {
@@ -39,6 +41,10 @@ interface SummaryChunk {
summary: Summary;
}
+const minChatsForSimilarity = 3;
+const maxSummarizationFailures = 3;
+const summarySeparator = "\n\n";
+
// Helper function to check if one Set is a subset of another
function isSubset(subset: Set, superset: Set): boolean {
for (const elem of subset) {
@@ -51,6 +57,7 @@ function isSubset(subset: Set, superset: Set): boolean {
function toSerializableHypaV3Data(data: HypaV3Data): SerializableHypaV3Data {
return {
+ ...data,
summaries: data.summaries.map((summary) => ({
...summary,
chatMemos: [...summary.chatMemos],
@@ -60,6 +67,7 @@ function toSerializableHypaV3Data(data: HypaV3Data): SerializableHypaV3Data {
function toHypaV3Data(serialData: SerializableHypaV3Data): HypaV3Data {
return {
+ ...serialData,
summaries: serialData.summaries.map((summary) => ({
...summary,
// Convert null back to undefined (JSON serialization converts undefined to null)
@@ -87,14 +95,17 @@ function cleanOrphanedSummary(chats: OpenAIChat[], data: HypaV3Data): void {
const removedCount = originalLength - data.summaries.length;
if (removedCount > 0) {
- console.log(`[HypaV3] Cleaned ${removedCount} orphaned summaries`);
+ console.log(`[HypaV3] Cleaned ${removedCount} orphaned summaries.`);
}
}
export async function summarize(
- stringifiedChats: string
+ oaiChats: OpenAIChat[]
): Promise<{ success: boolean; data: string }> {
const db = getDatabase();
+ const stringifiedChats = oaiChats
+ .map((chat) => `${chat.role}: ${chat.content}`)
+ .join("\n");
if (db.supaModelType === "distilbart") {
try {
@@ -103,7 +114,7 @@ export async function summarize(
} catch (error) {
return {
success: false,
- data: "[HypaV3] " + error,
+ data: error,
};
}
}
@@ -116,7 +127,7 @@ export async function summarize(
switch (db.supaModelType) {
case "instruct35": {
console.log(
- "[HypaV3] Using openAI gpt-3.5-turbo-instruct for summarization"
+ "[HypaV3] Using openAI gpt-3.5-turbo-instruct for summarization."
);
const requestPrompt = `${stringifiedChats}\n\n${summarizePrompt}\n\nOutput:`;
@@ -165,7 +176,7 @@ export async function summarize(
}
case "subModel": {
- console.log(`[HypaV3] Using ax model ${db.subModel} for summarization`);
+ console.log(`[HypaV3] Using ax model ${db.subModel} for summarization.`);
const requestMessages: OpenAIChat[] = parseChatML(
summarizePrompt.replaceAll("{{slot}}", stringifiedChats)
@@ -190,14 +201,17 @@ export async function summarize(
"memory"
);
- if (
- response.type === "fail" ||
- response.type === "streaming" ||
- response.type === "multiline"
- ) {
+ if (response.type === "streaming" || response.type === "multiline") {
return {
success: false,
- data: "Unexpected response type",
+ data: "unexpected response type",
+ };
+ }
+
+ if (response.type === "fail") {
+ return {
+ success: false,
+ data: response.result,
};
}
@@ -207,12 +221,43 @@ export async function summarize(
default: {
return {
success: false,
- data: `Unsupported model ${db.supaModelType} for summarization`,
+ data: `unsupported model ${db.supaModelType} for summarization`,
};
}
}
}
+async function retryableSummarize(
+ oaiChats: OpenAIChat[]
+): Promise<{ success: boolean; data: string }> {
+ let summarizationFailures = 0;
+
+ while (summarizationFailures < maxSummarizationFailures) {
+ console.log(
+ "[HypaV3] Attempting summarization:",
+ "\nAttempt:",
+ summarizationFailures + 1,
+ "\nTarget:",
+ oaiChats
+ );
+
+ const summarizeResult = await summarize(oaiChats);
+
+ if (!summarizeResult.success) {
+ console.log("[HypaV3] Summarization failed:", summarizeResult.data);
+ summarizationFailures++;
+
+ if (summarizationFailures >= maxSummarizationFailures) {
+ return summarizeResult;
+ }
+
+ continue;
+ }
+
+ return summarizeResult;
+ }
+}
+
export async function hypaMemoryV3(
chats: OpenAIChat[],
currentTokens: number,
@@ -226,9 +271,6 @@ export async function hypaMemoryV3(
error?: string;
memory?: SerializableHypaV3Data;
}> {
- const minChatsForSimilarity = 3;
- const maxSummarizationFailures = 3;
- const summarySeparator = "\n\n";
const db = getDatabase();
// Validate settings
@@ -250,6 +292,7 @@ export async function hypaMemoryV3(
// Load existing hypa data if available
let data: HypaV3Data = {
summaries: [],
+ lastSelectedSummaries: [],
};
if (room.hypaV3Data) {
@@ -360,8 +403,8 @@ export async function hypaMemoryV3(
i,
"\nRole:",
chat.role,
- "\nContent:\n",
- chat.content,
+ "\nContent:",
+ "\n" + chat.content,
"\nTokens:",
chatTokens
);
@@ -387,53 +430,29 @@ export async function hypaMemoryV3(
currentTokens - toSummarizeTokens < targetTokens
) {
console.log(
- `[HypaV3] Stopping summarization: would reduce below target tokens (${currentTokens} - ${toSummarizeTokens} < ${targetTokens})`
+ `[HypaV3] Stopping summarization: currentTokens(${currentTokens}) - toSummarizeTokens(${toSummarizeTokens}) < targetTokens(${targetTokens})`
);
break;
}
// Attempt summarization
- let summarizationFailures = 0;
- const stringifiedChats = toSummarize
- .map((chat) => `${chat.role}: ${chat.content}`)
- .join("\n");
+ const summarizeResult = await retryableSummarize(toSummarize);
- while (summarizationFailures < maxSummarizationFailures) {
- console.log(
- "[HypaV3] Attempting summarization:",
- "\nAttempt:",
- summarizationFailures + 1,
- "\nTarget:",
- toSummarize
- );
-
- const summarizeResult = await summarize(stringifiedChats);
-
- if (!summarizeResult.success) {
- console.log("[HypaV3] Summarization failed:", summarizeResult.data);
- summarizationFailures++;
-
- if (summarizationFailures >= maxSummarizationFailures) {
- return {
- currentTokens,
- chats,
- error: "[HypaV3] Summarization failed after maximum retries",
- memory: toSerializableHypaV3Data(data),
- };
- }
-
- continue;
- }
-
- data.summaries.push({
- text: summarizeResult.data,
- chatMemos: new Set(toSummarize.map((chat) => chat.memo)),
- isImportant: false,
- });
-
- break;
+ if (!summarizeResult.success) {
+ return {
+ currentTokens,
+ chats,
+ error: `[HypaV3] Summarization failed after maximum retries: ${summarizeResult.data}`,
+ memory: toSerializableHypaV3Data(data),
+ };
}
+ data.summaries.push({
+ text: summarizeResult.data,
+ chatMemos: new Set(toSummarize.map((chat) => chat.memo)),
+ isImportant: false,
+ });
+
currentTokens -= toSummarizeTokens;
startIdx = endIdx;
}
@@ -529,9 +548,9 @@ export async function hypaMemoryV3(
selectedRecentSummaries.length,
"\nSummaries:",
selectedRecentSummaries,
- "\nReserved Recent Memory Tokens:",
+ "\nReserved Tokens:",
reservedRecentMemoryTokens,
- "\nConsumed Recent Memory Tokens:",
+ "\nConsumed Tokens:",
consumedRecentMemoryTokens
);
}
@@ -608,56 +627,33 @@ export async function hypaMemoryV3(
// (2) Summarized recent chat search
if (db.hypaV3Settings.enableSimilarityCorrection) {
- let summarizationFailures = 0;
+ // Attempt summarization
const recentChats = chats.slice(-minChatsForSimilarity);
- const stringifiedRecentChats = recentChats
- .map((chat) => `${chat.role}: ${chat.content}`)
- .join("\n");
+ const summarizeResult = await retryableSummarize(recentChats);
- while (summarizationFailures < maxSummarizationFailures) {
- console.log(
- "[HypaV3] Attempting summarization:",
- "\nAttempt:",
- summarizationFailures + 1,
- "\nTarget:",
- recentChats
- );
-
- const summarizeResult = await summarize(stringifiedRecentChats);
-
- if (!summarizeResult.success) {
- console.log("[HypaV3] Summarization failed:", summarizeResult.data);
- summarizationFailures++;
-
- if (summarizationFailures >= maxSummarizationFailures) {
- return {
- currentTokens,
- chats,
- error: "[HypaV3] Summarization failed after maximum retries",
- memory: toSerializableHypaV3Data(data),
- };
- }
-
- continue;
- }
-
- const searched = await processor.similaritySearchScoredEx(
- summarizeResult.data
- );
-
- for (const [chunk, similarity] of searched) {
- const summary = chunk.summary;
-
- scoredSummaries.set(
- summary,
- (scoredSummaries.get(summary) || 0) + similarity
- );
- }
-
- console.log("[HypaV3] Similarity corrected");
-
- break;
+ if (!summarizeResult.success) {
+ return {
+ currentTokens,
+ chats,
+ error: `[HypaV3] Summarization failed after maximum retries: ${summarizeResult.data}`,
+ memory: toSerializableHypaV3Data(data),
+ };
}
+
+ const searched = await processor.similaritySearchScoredEx(
+ summarizeResult.data
+ );
+
+ for (const [chunk, similarity] of searched) {
+ const summary = chunk.summary;
+
+ scoredSummaries.set(
+ summary,
+ (scoredSummaries.get(summary) || 0) + similarity
+ );
+ }
+
+ console.log("[HypaV3] Similarity corrected.");
}
// Sort in descending order
@@ -677,6 +673,8 @@ export async function hypaMemoryV3(
"[HypaV3] Trying to add similar summary:",
"\nSummary Tokens:",
summaryTokens,
+ "\nConsumed Similar Memory Tokens:",
+ consumedSimilarMemoryTokens,
"\nReserved Tokens:",
reservedSimilarMemoryTokens,
"\nWould exceed:",
@@ -689,7 +687,7 @@ export async function hypaMemoryV3(
reservedSimilarMemoryTokens
) {
console.log(
- `[HypaV3] Stopping similar memory selection: would exceed reserved tokens (${consumedSimilarMemoryTokens} + ${summaryTokens} > ${reservedSimilarMemoryTokens})`
+ `[HypaV3] Stopping similar memory selection: consumedSimilarMemoryTokens(${consumedSimilarMemoryTokens}) + summaryTokens(${summaryTokens}) > reservedSimilarMemoryTokens(${reservedSimilarMemoryTokens})`
);
break;
}
@@ -706,9 +704,9 @@ export async function hypaMemoryV3(
selectedSimilarSummaries.length,
"\nSummaries:",
selectedSimilarSummaries,
- "\nReserved Similar Memory Tokens:",
+ "\nReserved Tokens:",
reservedSimilarMemoryTokens,
- "\nConsumed Similar Memory Tokens:",
+ "\nConsumed Tokens:",
consumedSimilarMemoryTokens
);
}
@@ -770,9 +768,9 @@ export async function hypaMemoryV3(
selectedRandomSummaries.length,
"\nSummaries:",
selectedRandomSummaries,
- "\nReserved Random Memory Tokens:",
+ "\nReserved Tokens:",
reservedRandomMemoryTokens,
- "\nConsumed Random Memory Tokens:",
+ "\nConsumed Tokens:",
consumedRandomMemoryTokens
);
}
@@ -814,10 +812,15 @@ export async function hypaMemoryV3(
if (currentTokens > maxContextTokens) {
throw new Error(
- `[HypaV3] Unexpected input token count:\nCurrent Tokens:${currentTokens}\nMax Context Tokens:${maxContextTokens}`
+ `[HypaV3] Unexpected error: input token count (${currentTokens}) exceeds max context size (${maxContextTokens})`
);
}
+ // Save last selected summaries
+ data.lastSelectedSummaries = selectedSummaries.map((selectedSummary) =>
+ data.summaries.findIndex((summary) => summary === selectedSummary)
+ );
+
const newChats: OpenAIChat[] = [
{
role: "system",
@@ -854,7 +857,7 @@ class HypaProcesserEx extends HypaProcesser {
summaryChunkVectors: SummaryChunkVector[] = [];
// Calculate dot product similarity between two vectors
- similarity(a: VectorArray, b: VectorArray) {
+ similarity(a: VectorArray, b: VectorArray): number {
let dot = 0;
for (let i = 0; i < a.length; i++) {
@@ -864,7 +867,7 @@ class HypaProcesserEx extends HypaProcesser {
return dot;
}
- async addSummaryChunks(chunks: SummaryChunk[]) {
+ async addSummaryChunks(chunks: SummaryChunk[]): Promise {
// Maintain the superclass's caching structure by adding texts
const texts = chunks.map((chunk) => chunk.text);