From 25b507a01a36f779d4f63e884382d47c5f5d29d2 Mon Sep 17 00:00:00 2001 From: Dong Hyun Kim <146043537+ThePotioner@users.noreply.github.com> Date: Thu, 6 Feb 2025 18:33:43 +0900 Subject: [PATCH 01/14] refactor(parser): import only the required asset when in a node environment - refactor parseAdditionalAssets() - Modified the getClostMatch() to work well with Object to which it is dynamically added. --- src/ts/parser.svelte.ts | 142 +++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 54 deletions(-) diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index c6912d5e..4a235208 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -295,6 +295,14 @@ async function renderHighlightableMarkdown(data:string) { export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|video-img|source)::(.+?)}}/gms +async function replaceAsync(string, regexp, replacerFunction) { + const replacements = await Promise.all( + Array.from(string.matchAll(regexp), + match => replacerFunction(...match as any))); + let i = 0; + return string.replace(regexp, () => replacements[i++]); +} + async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){ const assetWidthString = (DBState.db.assetWidth && DBState.db.assetWidth !== -1 || DBState.db.assetWidth === 0) ? `max-width:${DBState.db.assetWidth}rem;` : '' @@ -306,15 +314,6 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c path:string }} = {} - if(char.additionalAssets){ - for(const asset of char.additionalAssets){ - const assetPath = await getFileSrc(asset[1]) - assetPaths[asset[0].toLocaleLowerCase()] = { - path: assetPath, - ext: asset[2] - } - } - } if(char.emotionImages){ for(const emo of char.emotionImages){ const emoPath = await getFileSrc(emo[1]) @@ -335,8 +334,23 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c } const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] let needsSourceAccess = false - data = data.replaceAll(assetRegex, (full:string, type:string, name:string) => { + data = await replaceAsync(data, assetRegex, async (full:string, type:string, name:string) => { name = name.toLocaleLowerCase() + if(char.additionalAssets){ + for(const asset of char.additionalAssets){ + if (trimmer(asset[0].toLocaleLowerCase()) === trimmer(name)){ + console.log("In parseAdditionalAssets() > Catch Asset : " + asset[0] + "\nIn parseAdditionalAssets() > Asset Path : " + asset[1]) + const assetPath = await getFileSrc(asset[1]) + assetPaths[name] = { + path: assetPath, + ext: asset[2] + } + } + } + } + console.log("In parseAdditionalAssets() > Name: " + name) + console.log("In parseAdditionalAssets() > Char Obj: ", char) + console.log("In parseAdditionalAssets() > Char Type: " + char.type) if(type === 'emotion'){ const path = emoPaths[name]?.path if(!path){ @@ -361,7 +375,7 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c return '' } - path = getClosestMatch(name, assetPaths) + path = await getClosestMatch(char, name, assetPaths) if(!path){ return '' @@ -411,63 +425,84 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c return data } -function getClosestMatch(name:string, assetPaths:{[key:string]:{path:string, ext?:string}}){ - - if(Object.keys(assetPaths).length === 0){ - return null - } - - //Levenshtein distance, new with 1d array - const dest = (a:string, b:string) => { - const h = a.length + 1 - const w = b.length + 1 - let d = new Int16Array(h * w) - for(let i=0;i DBState.db.assetMaxDifference){ return null } + + const assetPath = await getFileSrc(targetPath) + assetPaths[closest] = { + path: assetPath, + ext: targetExt + } + + console.log("In getClosestMatch() > assetPaths[closest]: " + assetPaths[closest]) + console.log("In getClosestMatch() > closet, closetDist: " + closest, closestDist) + return assetPaths[closest] } +//Levenshtein distance, new with 1d array +function getDistance(a:string, b:string) { + const h = a.length + 1 + const w = b.length + 1 + let d = new Int16Array(h * w) + for(let i=0;i { try { let text = Buffer.from(txt, 'hex').toString('utf-8') From b3587fc950428b7584b0c8cf29abd101d4598bbb Mon Sep 17 00:00:00 2001 From: Dong Hyun Kim <146043537+ThePotioner@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:38:57 +0900 Subject: [PATCH 02/14] refactor(parsor): cleanup & add getAssetSrc() - Modified getAssetSrc() to handle Emotional, Module, and AdditionalAssets. - Reduced if-for statement depth for improved readability. --- src/ts/parser.svelte.ts | 79 ++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index 4a235208..622b7ade 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -15,6 +15,7 @@ import type { OpenAIChat } from './process/index.svelte'; import hljs from 'highlight.js/lib/core' import 'highlight.js/styles/atom-one-dark.min.css' import { language } from 'src/lang'; +import type { key } from 'localforage'; const markdownItOptions = { html: true, @@ -303,6 +304,33 @@ async function replaceAsync(string, regexp, replacerFunction) { return string.replace(regexp, () => replacements[i++]); } +async function getAssetSrc(assetArr: string[][], name: string, assetPaths?: {[key: string]:{path: string, ext?: string}}, emoPaths?: {[key: string]:{path: string, ext?: string}}) { + name = name.toLocaleLowerCase() + if (assetPaths) { + for(const asset of assetArr){ + if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue; + console.log("In getAssets() > Catch Asset : " + asset[0]) + console.log("In getAssets() > Asset Path : " + asset[1]) + const assetPath = await getFileSrc(asset[1]) + assetPaths[name] = { + path: assetPath, + ext: asset[2] + } + } + } + if (emoPaths) { + for(const emo of assetArr){ + if (trimmer(emo[0].toLocaleLowerCase()) !== trimmer(name)) continue; + console.log("In getAssets() > Catch Asset(emo) : " + emo[0]) + console.log("In getAssets() > Asset Path(emo) : " + emo[1]) + const emoPath = await getFileSrc(emo[1]) + emoPaths[name] = { + path: emoPath, + } + } + } +} + async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){ const assetWidthString = (DBState.db.assetWidth && DBState.db.assetWidth !== -1 || DBState.db.assetWidth === 0) ? `max-width:${DBState.db.assetWidth}rem;` : '' @@ -314,39 +342,19 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c path:string }} = {} - if(char.emotionImages){ - for(const emo of char.emotionImages){ - const emoPath = await getFileSrc(emo[1]) - emoPaths[emo[0].toLocaleLowerCase()] = { - path: emoPath, - } - } - } - const moduleAssets = getModuleAssets() - if(moduleAssets.length > 0){ - for(const asset of moduleAssets){ - const assetPath = await getFileSrc(asset[1]) - assetPaths[asset[0].toLocaleLowerCase()] = { - path: assetPath, - ext: asset[2] - } - } - } const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] let needsSourceAccess = false + data = await replaceAsync(data, assetRegex, async (full:string, type:string, name:string) => { - name = name.toLocaleLowerCase() - if(char.additionalAssets){ - for(const asset of char.additionalAssets){ - if (trimmer(asset[0].toLocaleLowerCase()) === trimmer(name)){ - console.log("In parseAdditionalAssets() > Catch Asset : " + asset[0] + "\nIn parseAdditionalAssets() > Asset Path : " + asset[1]) - const assetPath = await getFileSrc(asset[1]) - assetPaths[name] = { - path: assetPath, - ext: asset[2] - } - } - } + const moduleAssets = getModuleAssets() + if (char.additionalAssets) { + await getAssetSrc(char.additionalAssets, name, assetPaths); + } + if (char.emotionImages) { + await getAssetSrc(char.emotionImages, name, assetPaths, emoPaths); + } + if (moduleAssets.length > 0) { + await getAssetSrc(moduleAssets, name, assetPaths); } console.log("In parseAdditionalAssets() > Name: " + name) console.log("In parseAdditionalAssets() > Char Obj: ", char) @@ -444,14 +452,6 @@ async function getClosestMatch(char: simpleCharacterArgument|character, name:str targetExt = asset[2] } } - - // for(const key in assetPaths){ - // const dist = getDistance(trimmedName, trimmer(key)) - // if(dist < closestDist){ - // closest = key - // closestDist = dist - // } - // } if(closestDist > DBState.db.assetMaxDifference){ return null @@ -463,7 +463,7 @@ async function getClosestMatch(char: simpleCharacterArgument|character, name:str ext: targetExt } - console.log("In getClosestMatch() > assetPaths[closest]: " + assetPaths[closest]) + console.log("In getClosestMatch() > assetPaths[closest]: ", assetPaths[closest]) console.log("In getClosestMatch() > closet, closetDist: " + closest, closestDist) return assetPaths[closest] @@ -499,7 +499,6 @@ function trimmer(str:string){ } } - return str.trim().replace(/[_ -.]/g, '') } From 997b4f70ce6e57760cd1c0e0aae0478835748a44 Mon Sep 17 00:00:00 2001 From: Dong Hyun Kim <146043537+ThePotioner@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:41:40 +0900 Subject: [PATCH 03/14] refactor(parser): cleanup & add getEmoSrc() --- src/ts/parser.svelte.ts | 51 +++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index 622b7ade..e3c5be18 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -293,7 +293,6 @@ async function renderHighlightableMarkdown(data:string) { } - export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|video-img|source)::(.+?)}}/gms async function replaceAsync(string, regexp, replacerFunction) { @@ -304,29 +303,24 @@ async function replaceAsync(string, regexp, replacerFunction) { return string.replace(regexp, () => replacements[i++]); } -async function getAssetSrc(assetArr: string[][], name: string, assetPaths?: {[key: string]:{path: string, ext?: string}}, emoPaths?: {[key: string]:{path: string, ext?: string}}) { +async function getAssetSrc(assetArr: string[][], name: string, assetPaths: {[key: string]:{path: string, ext?: string}}) { name = name.toLocaleLowerCase() - if (assetPaths) { - for(const asset of assetArr){ - if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue; - console.log("In getAssets() > Catch Asset : " + asset[0]) - console.log("In getAssets() > Asset Path : " + asset[1]) - const assetPath = await getFileSrc(asset[1]) - assetPaths[name] = { - path: assetPath, - ext: asset[2] - } + for (const asset of assetArr) { + if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue; + const assetPath = await getFileSrc(asset[1]) + assetPaths[asset[0].toLocaleLowerCase()] = { + path: assetPath, + ext: asset[2] } + return; } - if (emoPaths) { - for(const emo of assetArr){ - if (trimmer(emo[0].toLocaleLowerCase()) !== trimmer(name)) continue; - console.log("In getAssets() > Catch Asset(emo) : " + emo[0]) - console.log("In getAssets() > Asset Path(emo) : " + emo[1]) - const emoPath = await getFileSrc(emo[1]) - emoPaths[name] = { - path: emoPath, - } +} + +async function getEmoSrc(emoArr: string[][], emoPaths: {[key: string]:{path: string}}) { + for (const emo of emoArr) { + const emoPath = await getFileSrc(emo[1]) + emoPaths[emo[0].toLocaleLowerCase()] = { + path: emoPath, } } } @@ -342,6 +336,8 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c path:string }} = {} + if (char.emotionImages) getEmoSrc(char.emotionImages, emoPaths) + const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] let needsSourceAccess = false @@ -350,15 +346,10 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c if (char.additionalAssets) { await getAssetSrc(char.additionalAssets, name, assetPaths); } - if (char.emotionImages) { - await getAssetSrc(char.emotionImages, name, assetPaths, emoPaths); - } if (moduleAssets.length > 0) { await getAssetSrc(moduleAssets, name, assetPaths); } - console.log("In parseAdditionalAssets() > Name: " + name) - console.log("In parseAdditionalAssets() > Char Obj: ", char) - console.log("In parseAdditionalAssets() > Char Type: " + char.type) + if(type === 'emotion'){ const path = emoPaths[name]?.path if(!path){ @@ -366,6 +357,7 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c } return `${path}` } + if(type === 'source'){ needsSourceAccess = true switch(name){ @@ -377,7 +369,9 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c } } } + let path = assetPaths[name] + if(!path){ if(DBState.db.legacyMediaFindings){ return '' @@ -462,9 +456,6 @@ async function getClosestMatch(char: simpleCharacterArgument|character, name:str path: assetPath, ext: targetExt } - - console.log("In getClosestMatch() > assetPaths[closest]: ", assetPaths[closest]) - console.log("In getClosestMatch() > closet, closetDist: " + closest, closestDist) return assetPaths[closest] } From ffd904ab5c2b0ba03963e3f9ffb0c506e230f045 Mon Sep 17 00:00:00 2001 From: Dong Hyun Kim <146043537+ThePotioner@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:23:15 +0900 Subject: [PATCH 04/14] fix(parser): fix typo & cleanup --- src/ts/parser.svelte.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index e3c5be18..33e14f5d 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -298,21 +298,21 @@ export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|vi async function replaceAsync(string, regexp, replacerFunction) { const replacements = await Promise.all( Array.from(string.matchAll(regexp), - match => replacerFunction(...match as any))); + match => replacerFunction(...match as any))) let i = 0; - return string.replace(regexp, () => replacements[i++]); + return string.replace(regexp, () => replacements[i++]) } async function getAssetSrc(assetArr: string[][], name: string, assetPaths: {[key: string]:{path: string, ext?: string}}) { name = name.toLocaleLowerCase() for (const asset of assetArr) { - if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue; + if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue const assetPath = await getFileSrc(asset[1]) assetPaths[asset[0].toLocaleLowerCase()] = { path: assetPath, ext: asset[2] } - return; + return } } @@ -336,7 +336,7 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c path:string }} = {} - if (char.emotionImages) getEmoSrc(char.emotionImages, emoPaths) + if (char.emotionImages) await getEmoSrc(char.emotionImages, emoPaths) const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] let needsSourceAccess = false @@ -344,10 +344,10 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c data = await replaceAsync(data, assetRegex, async (full:string, type:string, name:string) => { const moduleAssets = getModuleAssets() if (char.additionalAssets) { - await getAssetSrc(char.additionalAssets, name, assetPaths); + await getAssetSrc(char.additionalAssets, name, assetPaths) } if (moduleAssets.length > 0) { - await getAssetSrc(moduleAssets, name, assetPaths); + await getAssetSrc(moduleAssets, name, assetPaths) } if(type === 'emotion'){ From e3f205cc310727903cdbcda2f146567032a07106 Mon Sep 17 00:00:00 2001 From: Dong Hyun Kim <146043537+ThePotioner@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:43:03 +0900 Subject: [PATCH 05/14] fix(parsor): remove incorrectly imported packages --- src/ts/parser.svelte.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index 33e14f5d..6cd15b20 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -15,7 +15,6 @@ import type { OpenAIChat } from './process/index.svelte'; import hljs from 'highlight.js/lib/core' import 'highlight.js/styles/atom-one-dark.min.css' import { language } from 'src/lang'; -import type { key } from 'localforage'; const markdownItOptions = { html: true, From ca387909eaaeac78848b79031c318da044fde5a5 Mon Sep 17 00:00:00 2001 From: RH+ Date: Fri, 7 Feb 2025 19:33:31 +0900 Subject: [PATCH 06/14] Fix: Replace /sw/check to /sw/img in service worker checkCache --- public/sw.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/public/sw.js b/public/sw.js index ed1172cb..5d4296d1 100644 --- a/public/sw.js +++ b/public/sw.js @@ -87,8 +87,19 @@ self.addEventListener('fetch', (event) => { async function checkCache(url){ const cache = await caches.open('risuCache') + const origURL = url.pathname; + + if(url.pathname.startsWith("/sw/check")) { + url.pathname = "/sw/img" + url.pathname.slice(9); + return new Response(JSON.stringify({ + "able": !!(await cache.match(url)), + "origURL": origURL, + })) + } + return new Response(JSON.stringify({ - "able": !!(await cache.match(url)) + "able": !!(await cache.match(url)), + "origURL": origURL, })) } From 90c18f64904b0e3d16a45ad29348765b7d8aa8db Mon Sep 17 00:00:00 2001 From: RH+ Date: Fri, 7 Feb 2025 19:49:04 +0900 Subject: [PATCH 07/14] Fix: Remove url for debugging --- public/sw.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/public/sw.js b/public/sw.js index 5d4296d1..6dc58c71 100644 --- a/public/sw.js +++ b/public/sw.js @@ -87,19 +87,15 @@ self.addEventListener('fetch', (event) => { async function checkCache(url){ const cache = await caches.open('risuCache') - const origURL = url.pathname; - if(url.pathname.startsWith("/sw/check")) { url.pathname = "/sw/img" + url.pathname.slice(9); return new Response(JSON.stringify({ - "able": !!(await cache.match(url)), - "origURL": origURL, + "able": !!(await cache.match(url)) })) } return new Response(JSON.stringify({ - "able": !!(await cache.match(url)), - "origURL": origURL, + "able": !!(await cache.match(url)) })) } From 6d79f45d32c2bbcab772db99b08c6a081cbcd2fa Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Fri, 7 Feb 2025 21:08:50 +0900 Subject: [PATCH 08/14] feat: add doNotSummarizeUserChat option in HypaV3 - Add doNotSummarizeUserChat option to exclude user messages from summarization - Add early return logic to prevent unnecessary similarity checks when summaries are empty --- src/lib/Setting/Pages/OtherBotSettings.svelte | 11 ++++-- src/ts/process/memory/hypav3.ts | 37 +++++++++++++++++++ src/ts/storage/database.svelte.ts | 4 +- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index fe450fc4..418cb3a6 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -522,11 +522,11 @@
- Max Memory Tokens Ratio (Estimated) {#await getMaxMemoryRatio() then maxMemoryRatio} + Max Memory Tokens Ratio (Estimated) {:catch error} - {error} + Unable to calculate Max Memory Tokens Ratio {/await} Memory Tokens Ratio @@ -547,8 +547,11 @@
- -
+ + +
+ +
{:else if (DBState.db.supaModelType !== 'none' && DBState.db.hypav2 === false && DBState.db.hypaV3 === false)} {language.supaDesc} {language.SuperMemory} {language.model} diff --git a/src/ts/process/memory/hypav3.ts b/src/ts/process/memory/hypav3.ts index 91bef8d6..d9ea04a8 100644 --- a/src/ts/process/memory/hypav3.ts +++ b/src/ts/process/memory/hypav3.ts @@ -421,6 +421,12 @@ export async function hypaMemoryV3( continue; } + if (db.hypaV3Settings.doNotSummarizeUserChat && chat.role === "user") { + console.log(`[HypaV3] Skipping user role at index ${i}`); + + continue; + } + toSummarize.push(chat); } @@ -469,6 +475,37 @@ export async function hypaMemoryV3( availableMemoryTokens ); + // Early return if no summaries + if (data.summaries.length === 0) { + // Generate final memory prompt + const memory = encapsulateMemoryPrompt(""); + + const newChats: OpenAIChat[] = [ + { + role: "system", + content: memory, + memo: "supaMemory", + }, + ...chats.slice(startIdx), + ]; + + console.log( + "[HypaV3] Exiting function:", + "\nCurrent Tokens:", + currentTokens, + "\nAll chats, including memory prompt:", + newChats, + "\nMemory Data:", + data + ); + + return { + currentTokens, + chats: newChats, + memory: toSerializableHypaV3Data(data), + }; + } + const selectedSummaries: Summary[] = []; const randomMemoryRatio = 1 - diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 386be221..1e736cb9 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -480,7 +480,8 @@ export function setDatabase(data:Database){ similarMemoryRatio: data.hypaV3Settings?.similarMemoryRatio ?? 0.4, enableSimilarityCorrection: data.hypaV3Settings?.enableSimilarityCorrection ?? false, preserveOrphanedMemory: data.hypaV3Settings?.preserveOrphanedMemory ?? false, - processRegexScript: data.hypaV3Settings?.processRegexScript ?? false + processRegexScript: data.hypaV3Settings?.processRegexScript ?? false, + doNotSummarizeUserChat: data.hypaV3Settings?.doNotSummarizeUserChat ?? false } changeLanguage(data.language) setDatabaseLite(data) @@ -894,6 +895,7 @@ export interface Database{ enableSimilarityCorrection: boolean preserveOrphanedMemory: boolean processRegexScript: boolean + doNotSummarizeUserChat: boolean }, OaiCompAPIKeys: {[key:string]:string} inlayErrorResponse:boolean From 2fa11ebbe4594e986cf3272b7b3055c62d36de58 Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:31:41 +0900 Subject: [PATCH 09/14] fix: add length check before summarization in HypaV3 --- src/ts/process/memory/hypav3.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/ts/process/memory/hypav3.ts b/src/ts/process/memory/hypav3.ts index d9ea04a8..0316216a 100644 --- a/src/ts/process/memory/hypav3.ts +++ b/src/ts/process/memory/hypav3.ts @@ -442,23 +442,25 @@ export async function hypaMemoryV3( } // Attempt summarization - const summarizeResult = await retryableSummarize(toSummarize); + if (toSummarize.length > 0) { + const summarizeResult = await retryableSummarize(toSummarize); - if (!summarizeResult.success) { - return { - currentTokens, - chats, - error: `[HypaV3] Summarization failed after maximum retries: ${summarizeResult.data}`, - memory: toSerializableHypaV3Data(data), - }; + 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, + }); } - data.summaries.push({ - text: summarizeResult.data, - chatMemos: new Set(toSummarize.map((chat) => chat.memo)), - isImportant: false, - }); - currentTokens -= toSummarizeTokens; startIdx = endIdx; } From d36d8c86545e1a5251c02d10fcce173c92117d5e Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:43:20 +0900 Subject: [PATCH 10/14] feat: set to default settings when selecting HypaV3 on OtherBotSettings --- src/lib/Setting/Pages/OtherBotSettings.svelte | 13 +++++++++---- src/ts/storage/database.svelte.ts | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index 418cb3a6..00369004 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -467,6 +467,15 @@ DBState.db.hypav2 = false DBState.db.hanuraiEnable = false DBState.db.hypaV3 = true + DBState.db.hypaV3Settings.memoryTokensRatio = 0.2 + DBState.db.hypaV3Settings.extraSummarizationRatio = 0 + DBState.db.hypaV3Settings.maxChatsPerSummary = 4 + DBState.db.hypaV3Settings.recentMemoryRatio = 0.4 + DBState.db.hypaV3Settings.similarMemoryRatio = 0.4 + DBState.db.hypaV3Settings.enableSimilarityCorrection = false + DBState.db.hypaV3Settings.preserveOrphanedMemory = false + DBState.db.hypaV3Settings.processRegexScript = false + DBState.db.hypaV3Settings.doNotSummarizeUserChat = false } else { DBState.db.supaModelType = 'none' DBState.db.memoryAlgorithmType = 'none' @@ -514,10 +523,6 @@ distilbart-cnn-6-6 (Free/Local) {language.submodel} - {#if DBState.db.supaModelType === "instruct35"} - OpenAI API Key - - {/if} {language.summarizationPrompt}
diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 1e736cb9..44458554 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -474,7 +474,7 @@ export function setDatabase(data:Database){ data.reasoningEffort ??= 0 data.hypaV3Settings = { memoryTokensRatio: data.hypaV3Settings?.memoryTokensRatio ?? 0.2, - extraSummarizationRatio: data.hypaV3Settings?.extraSummarizationRatio ?? 0.2, + extraSummarizationRatio: data.hypaV3Settings?.extraSummarizationRatio ?? 0, maxChatsPerSummary: data.hypaV3Settings?.maxChatsPerSummary ?? 4, recentMemoryRatio: data.hypaV3Settings?.recentMemoryRatio ?? 0.4, similarMemoryRatio: data.hypaV3Settings?.similarMemoryRatio ?? 0.4, From 2d455840a492f2e8c2b9f85a4f0f6f4895a451f7 Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Sat, 8 Feb 2025 02:17:35 +0900 Subject: [PATCH 11/14] feat: add localization for HypaV3 settings and modal --- src/lang/cn.ts | 49 ++++++++++- src/lang/de.ts | 45 ++++++++++ src/lang/en.ts | 48 +++++++++- src/lang/es.ts | 45 ++++++++++ src/lang/ko.ts | 47 +++++++++- src/lang/vi.ts | 47 +++++++++- src/lang/zh-Hant.ts | 48 +++++++++- src/lib/Others/HypaV3Modal.svelte | 87 +++++++++++++------ src/lib/Setting/Pages/OtherBotSettings.svelte | 30 +++---- src/ts/process/memory/hypav3.ts | 2 +- src/ts/storage/database.svelte.ts | 4 +- 11 files changed, 399 insertions(+), 53 deletions(-) diff --git a/src/lang/cn.ts b/src/lang/cn.ts index 008ddce3..9e2596cd 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -770,5 +770,50 @@ export const languageChinese = { "translatorPrompt": "翻译提示词", "translateBeforeHTMLFormatting": "於 HTML 格式化前翻译", "retranslate": "重新翻译", - "loading": "加载中" - } + "loading": "加载中", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 是一个使用总结和向量搜索的长期记忆系统。", + "supaMemoryPromptPlaceHolder": "留空以使用默认值", + "maxMemoryTokensRatioLabel": "最大记忆令牌比率(估计)", + "maxMemoryTokensRatioError": "无法计算最大记忆令牌比率", + "memoryTokensRatioLabel": "记忆令牌比率", + "extraSummarizationRatioLabel": "额外总结比率", + "maxChatsPerSummaryLabel": "每个总结的最大消息数", + "recentMemoryRatioLabel": "最近记忆比率", + "similarMemoryRatioLabel": "相似记忆比率", + "randomMemoryRatioLabel": "随机记忆比率", + "enableSimilarityCorrectionLabel": "启用相似度校正", + "preserveOrphanedMemoryLabel": "保留孤立记忆", + "applyRegexScriptWhenRerollingLabel": "重新生成时应用正则脚本", + "doNotSummarizeUserMessageLabel": "不要总结用户消息", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3 数据", + "resetConfirmMessage": "此操作无法撤销。您要重置 HypaV3 数据吗?", + "resetConfirmSecondMessage": "此操作不可逆。您确实要重置 HypaV3 数据吗?", + "convertLabel": "尚无总结,但您可以将 HypaV2 数据转换为 V3。", + "convertButton": "转换为 V3", + "convertSuccessMessage": "成功将 HypaV2 数据转换为 V3", + "convertErrorMessage": "将 HypaV2 数据转换为 V3 失败:{0}", + "noSummariesLabel": "尚无总结", + "searchPlaceholder": "输入 #N、ID 或搜索查询", + "summaryNumberLabel": "总结 #{0}", + "deleteAfterConfirmMessage": "删除此后的所有总结?", + "deleteAfterConfirmSecondMessage": "此操作无法撤销。您确定吗?", + "translationLabel": "翻译", + "rerolledSummaryLabel": "重新生成的总结", + "rerolledTranslationLabel": "重新生成的总结翻译", + "connectedMessageCountLabel": "关联消息 ({0})", + "connectedFirstMessageLabel": "第一条消息", + "connectedMessageRoleLabel": "{0} 的消息", + "connectedMessageNotFoundLabel": "未找到消息", + "connectedMessageLoadingError": "加载关联消息时出错:{0}", + "connectedMessageTranslationLabel": "翻译", + "nextSummarizationFirstMessageLabel": "第一条消息", + "nextSummarizationNoMessageIdLabel": "无消息 ID", + "nextSummarizationLabel": "HypaV3 将总结 [{0}]", + "nextSummarizationNoMessagesFoundLabel": "警告:未找到消息", + "nextSummarizationLoadingError": "加载下一个总结目标时出错:{0}", + "emptySelectedFirstMessageLabel": "警告:所选的第一条消息为空" + }, +} diff --git a/src/lang/de.ts b/src/lang/de.ts index 8d0d8bc6..e463e436 100644 --- a/src/lang/de.ts +++ b/src/lang/de.ts @@ -435,4 +435,49 @@ export const languageGerman = { appendNameNAI: "Namen an NAI anhängen", customStopWords: "Benutzerdefinierte Stoppwörter", useAdvancedEditor: "Erweiterten Editor verwenden", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 ist ein Langzeitgedächtnissystem, das sowohl Zusammenfassung als auch Vektorsuche verwendet.", + "supaMemoryPromptPlaceHolder": "Leer lassen für Standardeinstellung", + "maxMemoryTokensRatioLabel": "Maximales Gedächtnis-Token-Verhältnis (Geschätzt)", + "maxMemoryTokensRatioError": "Maximales Gedächtnis-Token-Verhältnis kann nicht berechnet werden", + "memoryTokensRatioLabel": "Gedächtnis-Token-Verhältnis", + "extraSummarizationRatioLabel": "Zusätzliches Zusammenfassungsverhältnis", + "maxChatsPerSummaryLabel": "Maximale Nachrichten pro Zusammenfassung", + "recentMemoryRatioLabel": "Verhältnis der jüngsten Erinnerungen", + "similarMemoryRatioLabel": "Verhältnis ähnlicher Erinnerungen", + "randomMemoryRatioLabel": "Verhältnis zufälliger Erinnerungen", + "enableSimilarityCorrectionLabel": "Ähnlichkeitskorrektur aktivieren", + "preserveOrphanedMemoryLabel": "Verwaiste Erinnerungen bewahren", + "applyRegexScriptWhenRerollingLabel": "Regex-Skript beim Neugenerieren anwenden", + "doNotSummarizeUserMessageLabel": "Benutzernachrichten nicht zusammenfassen", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3 Daten", + "resetConfirmMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie die HypaV3-Daten zurücksetzen?", + "resetConfirmSecondMessage": "Diese Aktion ist unwiderruflich. Möchten Sie die HypaV3-Daten wirklich zurücksetzen?", + "convertLabel": "Noch keine Zusammenfassungen, aber Sie können HypaV2-Daten zu V3 konvertieren.", + "convertButton": "Zu V3 konvertieren", + "convertSuccessMessage": "HypaV2-Daten erfolgreich zu V3 konvertiert", + "convertErrorMessage": "Konvertierung von HypaV2 zu V3 fehlgeschlagen: {0}", + "noSummariesLabel": "Noch keine Zusammenfassungen", + "searchPlaceholder": "Geben Sie #N, ID oder Suchanfrage ein", + "summaryNumberLabel": "Zusammenfassung #{0}", + "deleteAfterConfirmMessage": "Alle Zusammenfassungen nach dieser löschen?", + "deleteAfterConfirmSecondMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Sind Sie wirklich sicher?", + "translationLabel": "Übersetzung", + "rerolledSummaryLabel": "Neu generierte Zusammenfassung", + "rerolledTranslationLabel": "Übersetzung der neu generierten Zusammenfassung", + "connectedMessageCountLabel": "Verbundene Nachrichten ({0})", + "connectedFirstMessageLabel": "Erste Nachricht", + "connectedMessageRoleLabel": "Nachricht von {0}", + "connectedMessageNotFoundLabel": "Nachricht nicht gefunden", + "connectedMessageLoadingError": "Fehler beim Laden der verbundenen Nachricht: {0}", + "connectedMessageTranslationLabel": "Übersetzung", + "nextSummarizationFirstMessageLabel": "Erste Nachricht", + "nextSummarizationNoMessageIdLabel": "Keine Nachrichten-ID", + "nextSummarizationLabel": "HypaV3 wird [{0}] zusammenfassen", + "nextSummarizationNoMessagesFoundLabel": "WARNUNG: Keine Nachrichten gefunden", + "nextSummarizationLoadingError": "Fehler beim Laden des nächsten Zusammenfassungsziels: {0}", + "emptySelectedFirstMessageLabel": "WARNUNG: Ausgewählte erste Nachricht ist leer" + }, } diff --git a/src/lang/en.ts b/src/lang/en.ts index a034db13..5aa8c57e 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -841,6 +841,50 @@ export const languageEnglish = { banCharacterset: 'Auto Regenerate On Characterset', checkCorruption: "Check Corruption", showPromptComparison: "Show Prompt Comparison", - hypaV3Desc: "HypaMemory V3 is a long-term memory system that use both summarized data and vector search.", inlayErrorResponse: "Inlay Error Response", -} \ No newline at end of file + hypaV3Settings: { + descriptionLabel: "HypaMemory V3 is a long-term memory system that uses both summarization and vector search.", + supaMemoryPromptPlaceHolder: "Leave it blank to use default", + maxMemoryTokensRatioLabel: "Max Memory Tokens Ratio (Estimated)", + maxMemoryTokensRatioError: "Unable to calculate Max Memory Tokens Ratio", + memoryTokensRatioLabel: "Memory Tokens Ratio", + extraSummarizationRatioLabel: "Extra Summarization Ratio", + maxChatsPerSummaryLabel: "Max Messages Per Summary", + recentMemoryRatioLabel: "Recent Memory Ratio", + similarMemoryRatioLabel: "Similar Memory Ratio", + randomMemoryRatioLabel: "Random Memory Ratio", + enableSimilarityCorrectionLabel: "Enable Similarity Correction", + preserveOrphanedMemoryLabel: "Preserve Orphaned Memory", + applyRegexScriptWhenRerollingLabel: "Apply Regex Script When Rerolling", + doNotSummarizeUserMessageLabel: "Do Not Summarize User Message", + }, + hypaV3Modal: { + titleLabel: "HypaV3 Data", + resetConfirmMessage: "This action cannot be undone. Do you want to reset HypaV3 data?", + resetConfirmSecondMessage: "This action is irreversible. Do you really, really want to reset HypaV3 data?", + convertLabel: "No summaries yet, but you may convert HypaV2 data to V3.", + convertButton: "Convert to V3", + convertSuccessMessage: "Successfully converted HypaV2 data to V3", + convertErrorMessage: "Failed to convert HypaV2 data to V3: {0}", + noSummariesLabel: "No summaries yet", + searchPlaceholder: "Enter #N, ID, or search query", + summaryNumberLabel: "Summary #{0}", + deleteAfterConfirmMessage: "Delete all summaries after this one?", + deleteAfterConfirmSecondMessage: "This action cannot be undone. Are you really sure?", + translationLabel: "Translation", + rerolledSummaryLabel: "Rerolled Summary", + rerolledTranslationLabel: "Rerolled Summary Translation", + connectedMessageCountLabel: "Connected Messages ({0})", + connectedFirstMessageLabel: "First Message", + connectedMessageRoleLabel: "{0}'s Message", + connectedMessageNotFoundLabel: "Message not found", + connectedMessageLoadingError: "Error loading connected message: {0}", + connectedMessageTranslationLabel: "Translation", + nextSummarizationFirstMessageLabel: "First Message", + nextSummarizationNoMessageIdLabel: "No Message ID", + nextSummarizationLabel: "HypaV3 will summarize [{0}]", + nextSummarizationNoMessagesFoundLabel: "WARN: No messages found", + nextSummarizationLoadingError: "Error loading next summarization target: {0}", + emptySelectedFirstMessageLabel: "WARN: Selected first message is empty", + }, +} diff --git a/src/lang/es.ts b/src/lang/es.ts index 9d36fbcf..9f776024 100644 --- a/src/lang/es.ts +++ b/src/lang/es.ts @@ -680,4 +680,49 @@ export const languageSpanish = { parameters: "Parámetros", sizeAndSpeed: "Tamaño y Velocidad", useLegacyGUI: "Usar Interfaz Legacy", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 es un sistema de memoria a largo plazo que utiliza tanto resúmenes como búsqueda vectorial.", + "supaMemoryPromptPlaceHolder": "Dejar en blanco para usar el valor predeterminado", + "maxMemoryTokensRatioLabel": "Ratio Máximo de Tokens de Memoria (Estimado)", + "maxMemoryTokensRatioError": "No se puede calcular el ratio máximo de tokens de memoria", + "memoryTokensRatioLabel": "Ratio de Tokens de Memoria", + "extraSummarizationRatioLabel": "Ratio de Resumen Adicional", + "maxChatsPerSummaryLabel": "Mensajes Máximos por Resumen", + "recentMemoryRatioLabel": "Ratio de Memoria Reciente", + "similarMemoryRatioLabel": "Ratio de Memoria Similar", + "randomMemoryRatioLabel": "Ratio de Memoria Aleatoria", + "enableSimilarityCorrectionLabel": "Activar Corrección de Similitud", + "preserveOrphanedMemoryLabel": "Preservar Memoria Huérfana", + "applyRegexScriptWhenRerollingLabel": "Aplicar Script Regex al Regenerar", + "doNotSummarizeUserMessageLabel": "No Resumir Mensajes del Usuario", + }, + "hypaV3Modal": { + "titleLabel": "Datos de HypaV3", + "resetConfirmMessage": "Esta acción no se puede deshacer. ¿Desea restablecer los datos de HypaV3?", + "resetConfirmSecondMessage": "Esta acción es irreversible. ¿Está realmente seguro de querer restablecer los datos de HypaV3?", + "convertLabel": "Aún no hay resúmenes, pero puede convertir datos de HypaV2 a V3.", + "convertButton": "Convertir a V3", + "convertSuccessMessage": "Datos de HypaV2 convertidos exitosamente a V3", + "convertErrorMessage": "Error al convertir datos de HypaV2 a V3: {0}", + "noSummariesLabel": "Aún no hay resúmenes", + "searchPlaceholder": "Ingrese #N, ID o consulta de búsqueda", + "summaryNumberLabel": "Resumen #{0}", + "deleteAfterConfirmMessage": "¿Eliminar todos los resúmenes después de este?", + "deleteAfterConfirmSecondMessage": "Esta acción no se puede deshacer. ¿Está realmente seguro?", + "translationLabel": "Traducción", + "rerolledSummaryLabel": "Resumen Regenerado", + "rerolledTranslationLabel": "Traducción del Resumen Regenerado", + "connectedMessageCountLabel": "Mensajes Conectados ({0})", + "connectedFirstMessageLabel": "Primer Mensaje", + "connectedMessageRoleLabel": "Mensaje de {0}", + "connectedMessageNotFoundLabel": "Mensaje no encontrado", + "connectedMessageLoadingError": "Error al cargar mensaje conectado: {0}", + "connectedMessageTranslationLabel": "Traducción", + "nextSummarizationFirstMessageLabel": "Primer Mensaje", + "nextSummarizationNoMessageIdLabel": "Sin ID de Mensaje", + "nextSummarizationLabel": "HypaV3 resumirá [{0}]", + "nextSummarizationNoMessagesFoundLabel": "ADVERTENCIA: No se encontraron mensajes", + "nextSummarizationLoadingError": "Error al cargar el siguiente objetivo de resumen: {0}", + "emptySelectedFirstMessageLabel": "ADVERTENCIA: El primer mensaje seleccionado está vacío" + }, } diff --git a/src/lang/ko.ts b/src/lang/ko.ts index 9d3cfa0b..b100c8d4 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -767,4 +767,49 @@ export const languageKorean = { "translateBeforeHTMLFormatting": "HTML 포맷 전 번역", "retranslate": "다시 번역", "loading": "로딩중", -} \ No newline at end of file + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3는 요약과 벡터 검색을 모두 사용하는 장기 기억 시스템입니다.", + "supaMemoryPromptPlaceHolder": "기본값을 사용하려면 비워두세요", + "maxMemoryTokensRatioLabel": "최대 메모리 토큰 비율 (추정)", + "maxMemoryTokensRatioError": "최대 메모리 토큰 비율을 계산할 수 없습니다", + "memoryTokensRatioLabel": "메모리 토큰 비율", + "extraSummarizationRatioLabel": "추가 요약 비율", + "maxChatsPerSummaryLabel": "요약당 최대 메시지 수", + "recentMemoryRatioLabel": "최근 메모리 비율", + "similarMemoryRatioLabel": "유사 메모리 비율", + "randomMemoryRatioLabel": "무작위 메모리 비율", + "enableSimilarityCorrectionLabel": "유사도 보정 활성화", + "preserveOrphanedMemoryLabel": "고아 메모리 보존", + "applyRegexScriptWhenRerollingLabel": "재생성 시 정규식 스크립트 적용", + "doNotSummarizeUserMessageLabel": "유저 메시지 요약하지 않기", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3 데이터", + "resetConfirmMessage": "이 작업은 되돌릴 수 없습니다. HypaV3 데이터를 초기화하시겠습니까?", + "resetConfirmSecondMessage": "이 작업은 복구할 수 없습니다. 정말로, 정말로 HypaV3 데이터를 초기화하시겠습니까?", + "convertLabel": "아직 요약이 없지만, HypaV2 데이터를 V3로 변환할 수 있습니다.", + "convertButton": "V3로 변환", + "convertSuccessMessage": "HypaV2 데이터를 V3로 성공적으로 변환했습니다", + "convertErrorMessage": "HypaV2 데이터를 V3로 변환하는데 실패했습니다: {0}", + "noSummariesLabel": "아직 요약이 없습니다", + "searchPlaceholder": "#N, ID 또는 검색어를 입력하세요", + "summaryNumberLabel": "요약 #{0}", + "deleteAfterConfirmMessage": "이 요약 이후의 모든 요약을 삭제하시겠습니까?", + "deleteAfterConfirmSecondMessage": "이 작업은 되돌릴 수 없습니다. 정말 삭제하시겠습니까?", + "translationLabel": "번역", + "rerolledSummaryLabel": "재생성된 요약", + "rerolledTranslationLabel": "재생성된 요약 번역", + "connectedMessageCountLabel": "연결된 메시지 ({0})", + "connectedFirstMessageLabel": "첫 메시지", + "connectedMessageRoleLabel": "{0}의 메시지", + "connectedMessageNotFoundLabel": "메시지를 찾을 수 없습니다", + "connectedMessageLoadingError": "연결된 메시지를 불러오는 동안 오류 발생: {0}", + "connectedMessageTranslationLabel": "번역", + "nextSummarizationFirstMessageLabel": "첫 메시지", + "nextSummarizationNoMessageIdLabel": "메시지 ID 없음", + "nextSummarizationLabel": "HypaV3가 [{0}]를 요약할 예정입니다", + "nextSummarizationNoMessagesFoundLabel": "경고: 메시지를 찾을 수 없습니다", + "nextSummarizationLoadingError": "다음 요약 대상을 불러오는 동안 오류 발생: {0}", + "emptySelectedFirstMessageLabel": "경고: 선택된 첫 메시지가 비어있습니다" + }, +} diff --git a/src/lang/vi.ts b/src/lang/vi.ts index 6d7133a9..c9765433 100644 --- a/src/lang/vi.ts +++ b/src/lang/vi.ts @@ -409,4 +409,49 @@ export const LanguageVietnamese = { module: "Mô-đun", modules: "Mô-đun", useAdvancedEditor: "Sử dụng trình biên tập nâng cao", -} \ No newline at end of file + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 là hệ thống bộ nhớ dài hạn sử dụng cả tóm tắt và tìm kiếm vector.", + "supaMemoryPromptPlaceHolder": "Để trống để sử dụng giá trị mặc định", + "maxMemoryTokensRatioLabel": "Tỷ lệ Token Bộ nhớ Tối đa (Ước tính)", + "maxMemoryTokensRatioError": "Không thể tính toán Tỷ lệ Token Bộ nhớ Tối đa", + "memoryTokensRatioLabel": "Tỷ lệ Token Bộ nhớ", + "extraSummarizationRatioLabel": "Tỷ lệ Tóm tắt Bổ sung", + "maxChatsPerSummaryLabel": "Số Tin nhắn Tối đa cho mỗi Tóm tắt", + "recentMemoryRatioLabel": "Tỷ lệ Bộ nhớ Gần đây", + "similarMemoryRatioLabel": "Tỷ lệ Bộ nhớ Tương tự", + "randomMemoryRatioLabel": "Tỷ lệ Bộ nhớ Ngẫu nhiên", + "enableSimilarityCorrectionLabel": "Bật Hiệu chỉnh Độ tương tự", + "preserveOrphanedMemoryLabel": "Giữ Bộ nhớ Mồ côi", + "applyRegexScriptWhenRerollingLabel": "Áp dụng Script Regex khi Tạo lại", + "doNotSummarizeUserMessageLabel": "Không Tóm tắt Tin nhắn Người dùng", + }, + "hypaV3Modal": { + "titleLabel": "Dữ liệu HypaV3", + "resetConfirmMessage": "Hành động này không thể hoàn tác. Bạn có muốn đặt lại dữ liệu HypaV3 không?", + "resetConfirmSecondMessage": "Hành động này không thể khôi phục. Bạn có thực sự chắc chắn muốn đặt lại dữ liệu HypaV3 không?", + "convertLabel": "Chưa có tóm tắt nào, nhưng bạn có thể chuyển đổi dữ liệu HypaV2 sang V3.", + "convertButton": "Chuyển đổi sang V3", + "convertSuccessMessage": "Đã chuyển đổi thành công dữ liệu HypaV2 sang V3", + "convertErrorMessage": "Chuyển đổi dữ liệu HypaV2 sang V3 thất bại: {0}", + "noSummariesLabel": "Chưa có tóm tắt nào", + "searchPlaceholder": "Nhập #N, ID, hoặc từ khóa tìm kiếm", + "summaryNumberLabel": "Tóm tắt #{0}", + "deleteAfterConfirmMessage": "Xóa tất cả các tóm tắt sau tóm tắt này?", + "deleteAfterConfirmSecondMessage": "Hành động này không thể hoàn tác. Bạn có chắc chắn không?", + "translationLabel": "Bản dịch", + "rerolledSummaryLabel": "Tóm tắt đã Tạo lại", + "rerolledTranslationLabel": "Bản dịch Tóm tắt đã Tạo lại", + "connectedMessageCountLabel": "Tin nhắn Liên kết ({0})", + "connectedFirstMessageLabel": "Tin nhắn Đầu tiên", + "connectedMessageRoleLabel": "Tin nhắn của {0}", + "connectedMessageNotFoundLabel": "Không tìm thấy tin nhắn", + "connectedMessageLoadingError": "Lỗi khi tải tin nhắn liên kết: {0}", + "connectedMessageTranslationLabel": "Bản dịch", + "nextSummarizationFirstMessageLabel": "Tin nhắn Đầu tiên", + "nextSummarizationNoMessageIdLabel": "Không có ID Tin nhắn", + "nextSummarizationLabel": "HypaV3 sẽ tóm tắt [{0}]", + "nextSummarizationNoMessagesFoundLabel": "CẢNH BÁO: Không tìm thấy tin nhắn", + "nextSummarizationLoadingError": "Lỗi khi tải mục tiêu tóm tắt tiếp theo: {0}", + "emptySelectedFirstMessageLabel": "CẢNH BÁO: Tin nhắn đầu tiên được chọn trống" + }, +} diff --git a/src/lang/zh-Hant.ts b/src/lang/zh-Hant.ts index fb386234..3f580f64 100644 --- a/src/lang/zh-Hant.ts +++ b/src/lang/zh-Hant.ts @@ -802,7 +802,51 @@ export const languageChineseTraditional = { "banCharacterset": "自動重新生成字符集", "checkCorruption": "檢查損壞", "showPromptComparison": "顯示提示比較", - "hypaV3Desc": "HypaMemory V3 是一個長期記憶系統,使用摘要資料和向量搜尋。", "inlayErrorResponse": "嵌入錯誤回應", - "APIPool": "API 工具" + "APIPool": "API 工具", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 是一個使用摘要和向量搜索的長期記憶系統。", + "supaMemoryPromptPlaceHolder": "留空以使用預設值", + "maxMemoryTokensRatioLabel": "最大記憶標記比率(估計)", + "maxMemoryTokensRatioError": "無法計算最大記憶標記比率", + "memoryTokensRatioLabel": "記憶標記比率", + "extraSummarizationRatioLabel": "額外摘要比率", + "maxChatsPerSummaryLabel": "每個摘要的最大訊息數", + "recentMemoryRatioLabel": "最近記憶比率", + "similarMemoryRatioLabel": "相似記憶比率", + "randomMemoryRatioLabel": "隨機記憶比率", + "enableSimilarityCorrectionLabel": "啟用相似度校正", + "preserveOrphanedMemoryLabel": "保留孤立記憶", + "applyRegexScriptWhenRerollingLabel": "重新生成時應用正則表達式腳本", + "doNotSummarizeUserMessageLabel": "不要摘要用戶訊息" + }, + "hypaV3Modal": { + "titleLabel": "HypaV3 數據", + "resetConfirmMessage": "此操作無法撤銷。您要重置 HypaV3 數據嗎?", + "resetConfirmSecondMessage": "此操作不可逆。您真的確定要重置 HypaV3 數據嗎?", + "convertLabel": "尚無摘要,但您可以將 HypaV2 數據轉換為 V3。", + "convertButton": "轉換為 V3", + "convertSuccessMessage": "成功將 HypaV2 數據轉換為 V3", + "convertErrorMessage": "無法將 HypaV2 數據轉換為 V3:{0}", + "noSummariesLabel": "尚無摘要", + "searchPlaceholder": "輸入 #N、ID 或搜索查詢", + "summaryNumberLabel": "摘要 #{0}", + "deleteAfterConfirmMessage": "刪除此摘要之後的所有摘要?", + "deleteAfterConfirmSecondMessage": "此操作無法撤銷。您確定要這樣做嗎?", + "translationLabel": "翻譯", + "rerolledSummaryLabel": "重新生成的摘要", + "rerolledTranslationLabel": "重新生成的摘要翻譯", + "connectedMessageCountLabel": "關聯訊息({0})", + "connectedFirstMessageLabel": "第一條訊息", + "connectedMessageRoleLabel": "{0} 的訊息", + "connectedMessageNotFoundLabel": "找不到訊息", + "connectedMessageLoadingError": "載入關聯訊息時出錯:{0}", + "connectedMessageTranslationLabel": "翻譯", + "nextSummarizationFirstMessageLabel": "第一條訊息", + "nextSummarizationNoMessageIdLabel": "無訊息 ID", + "nextSummarizationLabel": "HypaV3 將摘要 [{0}]", + "nextSummarizationNoMessagesFoundLabel": "警告:找不到訊息", + "nextSummarizationLoadingError": "載入下一個摘要目標時出錯:{0}", + "emptySelectedFirstMessageLabel": "警告:選定的第一條訊息為空" + }, } diff --git a/src/lib/Others/HypaV3Modal.svelte b/src/lib/Others/HypaV3Modal.svelte index ea9b6616..03bbb14a 100644 --- a/src/lib/Others/HypaV3Modal.svelte +++ b/src/lib/Others/HypaV3Modal.svelte @@ -28,6 +28,7 @@ import { summarize } from "../../ts/process/memory/hypav3"; import { type Message } from "../../ts/storage/database.svelte"; import { translateHTML } from "../../ts/translator/translator"; + import { language } from "../../lang"; interface SummaryUI { originalRef: HTMLTextAreaElement; @@ -759,7 +760,7 @@

- HypaV3 Data + {language.hypaV3Modal.titleLabel}

@@ -793,8 +794,8 @@ onclick={async () => { 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?" + language.hypaV3Modal.resetConfirmMessage, + language.hypaV3Modal.resetConfirmSecondMessage ) ) { DBState.db.characters[$selectedCharID].chats[ @@ -836,7 +837,7 @@ >
- No summaries yet, but you may convert HypaV2 data to V3. + {language.hypaV3Modal.convertLabel}
{:else}
- No summaries yet + {language.hypaV3Modal.noSummariesLabel}
{/if} @@ -880,7 +884,7 @@ > { @@ -921,7 +925,12 @@ >
- Summary #{i + 1} + {language.hypaV3Modal.summaryNumberLabel.replace( + "{0}", + (i + 1).toString() + )}
@@ -962,8 +971,8 @@ onclick={async () => { if ( await alertConfirmTwice( - "Delete all summaries after this one?", - "This action cannot be undone. Are you really sure?" + language.hypaV3Modal.deleteAfterConfirmMessage, + language.hypaV3Modal.deleteAfterConfirmSecondMessage ) ) { hypaV3DataState.summaries.splice(i + 1); @@ -991,7 +1000,7 @@ {#if summaryUIStates[i].translation}
- Translation + {language.hypaV3Modal.translationLabel}
{:else} - Message not found{language.hypaV3Modal + .connectedMessageNotFoundLabel} {/if} {:catch error} Error loading expanded message: {error.message}{language.hypaV3Modal.connectedMessageLoadingError.replace( + "{0}", + error.message + )} {/await}
@@ -1150,7 +1174,7 @@ {#if expandedMessageUIState.translation}
- Translation + {language.hypaV3Modal.connectedMessageTranslationLabel}
{:else} - WARN: No messages found + {language.hypaV3Modal + .nextSummarizationNoMessagesFoundLabel} {/if} {:catch error} Error loading next message: {error.message}{language.hypaV3Modal.nextSummarizationLoadingError.replace( + "{0}", + error.message + )} {/await}
@@ -1200,7 +1233,7 @@ {#if !getFirstMessage()}
WARN: Selected first message is empty{language.hypaV3Modal.emptySelectedFirstMessageLabel}
{/if} diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index 00369004..88003e0e 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -475,7 +475,7 @@ DBState.db.hypaV3Settings.enableSimilarityCorrection = false DBState.db.hypaV3Settings.preserveOrphanedMemory = false DBState.db.hypaV3Settings.processRegexScript = false - DBState.db.hypaV3Settings.doNotSummarizeUserChat = false + DBState.db.hypaV3Settings.doNotSummarizeUserMessage = false } else { DBState.db.supaModelType = 'none' DBState.db.memoryAlgorithmType = 'none' @@ -517,7 +517,7 @@ {language.hypaAllocatedTokens} {:else if DBState.db.hypaV3} - {language.hypaV3Desc} + {language.hypaV3Settings.descriptionLabel} {language.SuperMemory} {language.model} distilbart-cnn-6-6 (Free/Local) @@ -525,37 +525,37 @@ {language.summarizationPrompt}
- +
{#await getMaxMemoryRatio() then maxMemoryRatio} - Max Memory Tokens Ratio (Estimated) + {language.hypaV3Settings.maxMemoryTokensRatioLabel} {:catch error} - Unable to calculate Max Memory Tokens Ratio + {language.hypaV3Settings.maxMemoryTokensRatioError} {/await} - Memory Tokens Ratio + {language.hypaV3Settings.memoryTokensRatioLabel} - Extra Summarization Ratio + {language.hypaV3Settings.extraSummarizationRatioLabel} - Max Chats Per Summary + {language.hypaV3Settings.maxChatsPerSummaryLabel} - Recent Memory Ratio + {language.hypaV3Settings.recentMemoryRatioLabel} - Similar Memory Ratio + {language.hypaV3Settings.similarMemoryRatioLabel} - Random Memory Ratio + {language.hypaV3Settings.randomMemoryRatioLabel}
- +
- +
- +
- +
{:else if (DBState.db.supaModelType !== 'none' && DBState.db.hypav2 === false && DBState.db.hypaV3 === false)} {language.supaDesc} diff --git a/src/ts/process/memory/hypav3.ts b/src/ts/process/memory/hypav3.ts index 0316216a..1ce2489e 100644 --- a/src/ts/process/memory/hypav3.ts +++ b/src/ts/process/memory/hypav3.ts @@ -421,7 +421,7 @@ export async function hypaMemoryV3( continue; } - if (db.hypaV3Settings.doNotSummarizeUserChat && chat.role === "user") { + if (db.hypaV3Settings.doNotSummarizeUserMessage && chat.role === "user") { console.log(`[HypaV3] Skipping user role at index ${i}`); continue; diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 44458554..c5defa8b 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -481,7 +481,7 @@ export function setDatabase(data:Database){ enableSimilarityCorrection: data.hypaV3Settings?.enableSimilarityCorrection ?? false, preserveOrphanedMemory: data.hypaV3Settings?.preserveOrphanedMemory ?? false, processRegexScript: data.hypaV3Settings?.processRegexScript ?? false, - doNotSummarizeUserChat: data.hypaV3Settings?.doNotSummarizeUserChat ?? false + doNotSummarizeUserMessage: data.hypaV3Settings?.doNotSummarizeUserMessage ?? false } changeLanguage(data.language) setDatabaseLite(data) @@ -895,7 +895,7 @@ export interface Database{ enableSimilarityCorrection: boolean preserveOrphanedMemory: boolean processRegexScript: boolean - doNotSummarizeUserChat: boolean + doNotSummarizeUserMessage: boolean }, OaiCompAPIKeys: {[key:string]:string} inlayErrorResponse:boolean From 6b6be0504d16701c8322355cd27a86a98a9502e9 Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Sat, 8 Feb 2025 11:17:46 +0900 Subject: [PATCH 12/14] fix: display cached llm translations while sending message --- src/lang/en.ts | 4 ++-- src/lang/ko.ts | 4 +++- src/lang/zh-Hant.ts | 4 ++-- src/ts/translator/translator.ts | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index a034db13..55d99f16 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -170,7 +170,7 @@ export const languageEnglish = { summarizationPrompt: "The prompt that is used for summarization. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the chat data.", translatorPrompt: "The prompt that is used for translation. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the dest language, {{solt::content}} for the content, and {{slot::tnote}} for the translator note.", translateBeforeHTMLFormatting: "If enabled, it will translate the text before Regex scripts and HTML formatting. this could make the token lesser but could break the formatting.", - autoTranslateCachedOnly: "If enabled with Auto Translation option on, it will automatically translate only the text that the user has translated previously.", + autoTranslateCachedOnly: "If enabled with Auto Translation option on, it will automatically translate only the messages that the user has translated previously.", presetChain: "If it is not blank, the preset will be changed and applied randomly every time when user sends a message in the preset list in this input. preset list should be seperated by comma, for example, `preset1,preset2`.", legacyMediaFindings: "If enabled, it will use the old method to find media assets, without using the additional search algorithm.", comfyWorkflow: "Put the API workflow of comfy UI. you can get your API workflow in comfy UI by pressing the 'Workflow > Export (API)' button. you must also put {{risu_prompt}} in you workflow text. the {{risu_prompt}} will be replaced with the prompt provided by the Risu.", @@ -816,7 +816,7 @@ export const languageEnglish = { translateBeforeHTMLFormatting: "Translate Before HTML Formatting", retranslate: "Retranslate", loading: "Loading", - autoTranslateCachedOnly: "Auto Translate Cached Only", + autoTranslateCachedOnly: "Auto-translate Cached Messages Only", notification: "Notification", permissionDenied: "Permission Denied by Your Browser or OS", customFlags: "Custom Flags", diff --git a/src/lang/ko.ts b/src/lang/ko.ts index 9d3cfa0b..5d80ad82 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -123,7 +123,8 @@ export const languageKorean = { "translatorNote": "여기에서 캐릭터마다 별도의 번역 프롬프트를 넣을 수 있습니다. 해당 옵션은 Ax. model 번역을 사용할 때만 적용됩니다. 언어 설정에서 `{{slot::tnote}}`를 넣으세요. 그룹챗에서는 작동하지 않습니다.", "groupInnerFormat": "This defines a format that is used in group chat for characters that isn't speaker. if it is not blank, it will use this format instead of the default format. if `Group Other Bot Role` is `assistant`, it will also be applied to the speaker.", "groupOtherBotRole": "This defines a role that is used in group chat for characters that isn't speaker.", - "chatHTML": "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button." + "chatHTML": "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button.", + "autoTranslateCachedOnly": "자동 번역 옵션이 켜진 상태에서 활성화하면, 사용자가 이전에 번역한 메시지만 자동으로 번역됩니다.", }, "setup": { "chooseProvider": "AI 제공자를 선택해 주세요", @@ -767,4 +768,5 @@ export const languageKorean = { "translateBeforeHTMLFormatting": "HTML 포맷 전 번역", "retranslate": "다시 번역", "loading": "로딩중", + "autoTranslateCachedOnly": "캐시된 메시지만 자동 번역", } \ No newline at end of file diff --git a/src/lang/zh-Hant.ts b/src/lang/zh-Hant.ts index fb386234..48de6e93 100644 --- a/src/lang/zh-Hant.ts +++ b/src/lang/zh-Hant.ts @@ -129,7 +129,7 @@ export const languageChineseTraditional = { "summarizationPrompt": "用於摘要的提示詞。留空將使用預設提示詞。您也可以使用包含 {{slot}} 的 ChatML 格式來處理聊天數據。", "translatorPrompt": "用於翻譯的提示詞。留空將使用默認提示。您還可以使用帶有 {{slot}} 的 ChatML 格式表示目標語言:用 {{slot::content}} 表示內容,用 {{slot::tnote}} 表示翻譯註釋。", "translateBeforeHTMLFormatting": "啟用後,將在正規表達式和 HTML 格式化之前翻譯文本。這可能減少 Token 數,但可能破壞格式。", - "autoTranslateCachedOnly": "啟用後,僅會自動翻譯使用者之前已翻譯的內容。", + "autoTranslateCachedOnly": "啟用自動翻譯選項時,將只自動翻譯使用者先前已翻譯過的訊息。", "presetChain": "若欄位不為空,則每次使用者發送訊息時,系統會從此輸入中的預設列表中隨機變更並應用預設設定。預設列表應以逗號分隔,例如 `preset1,preset2`。", "legacyMediaFindings": "啟用後,將使用舊版方法尋找媒體資源,而不使用額外的搜尋演算法。", "comfyWorkflow": "請輸入 Comfy UI 的 API 工作流程。您可以在 Comfy UI 中點選「Workflow > Export (API)」按鈕以取得您的 API 工作流程。此外,您必須在工作流程文本中加入 {{risu_prompt}},該佔位符將被 Risu 提供的提示詞替換。", @@ -777,7 +777,7 @@ export const languageChineseTraditional = { "translateBeforeHTMLFormatting": "於 HTML 格式化前翻譯", "retranslate": "重新翻譯", "loading": "載入中", - "autoTranslateCachedOnly": "僅自動翻譯已快取的內容", + "autoTranslateCachedOnly": "僅自動翻譯已快取訊息", "notification": "使用系統通知", "permissionDenied": "權限被您的瀏覽器或操作系統拒絕", "customFlags": "自定義修飾詞(Flags)", diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index 542aae0b..1194ac4b 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -231,7 +231,9 @@ export async function translateHTML(html: string, reverse:boolean, charArg:simpl let DoingChat = get(doingChat) if(DoingChat){ if(isExpTranslator()){ - return html + if(!(db.translatorType === 'llm' && await getLLMCache(html) !== null)){ + return html + } } } if(db.translatorType === 'llm'){ From 1f03e17f11615af61d6f10f52bc2281dd92ad0b0 Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Sun, 9 Feb 2025 09:02:53 +0900 Subject: [PATCH 13/14] fix: change HypaV3 modal title --- src/lang/cn.ts | 2 +- src/lang/de.ts | 2 +- src/lang/en.ts | 2 +- src/lang/es.ts | 2 +- src/lang/ko.ts | 2 +- src/lang/vi.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lang/cn.ts b/src/lang/cn.ts index 9e2596cd..6e543d76 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -788,7 +788,7 @@ export const languageChinese = { "doNotSummarizeUserMessageLabel": "不要总结用户消息", }, "hypaV3Modal": { - "titleLabel": "HypaV3 数据", + "titleLabel": "HypaV3", "resetConfirmMessage": "此操作无法撤销。您要重置 HypaV3 数据吗?", "resetConfirmSecondMessage": "此操作不可逆。您确实要重置 HypaV3 数据吗?", "convertLabel": "尚无总结,但您可以将 HypaV2 数据转换为 V3。", diff --git a/src/lang/de.ts b/src/lang/de.ts index e463e436..41144dc6 100644 --- a/src/lang/de.ts +++ b/src/lang/de.ts @@ -452,7 +452,7 @@ export const languageGerman = { "doNotSummarizeUserMessageLabel": "Benutzernachrichten nicht zusammenfassen", }, "hypaV3Modal": { - "titleLabel": "HypaV3 Daten", + "titleLabel": "HypaV3", "resetConfirmMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie die HypaV3-Daten zurücksetzen?", "resetConfirmSecondMessage": "Diese Aktion ist unwiderruflich. Möchten Sie die HypaV3-Daten wirklich zurücksetzen?", "convertLabel": "Noch keine Zusammenfassungen, aber Sie können HypaV2-Daten zu V3 konvertieren.", diff --git a/src/lang/en.ts b/src/lang/en.ts index 5aa8c57e..760a8dcd 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -859,7 +859,7 @@ export const languageEnglish = { doNotSummarizeUserMessageLabel: "Do Not Summarize User Message", }, hypaV3Modal: { - titleLabel: "HypaV3 Data", + titleLabel: "HypaV3", resetConfirmMessage: "This action cannot be undone. Do you want to reset HypaV3 data?", resetConfirmSecondMessage: "This action is irreversible. Do you really, really want to reset HypaV3 data?", convertLabel: "No summaries yet, but you may convert HypaV2 data to V3.", diff --git a/src/lang/es.ts b/src/lang/es.ts index 9f776024..f0c5787c 100644 --- a/src/lang/es.ts +++ b/src/lang/es.ts @@ -697,7 +697,7 @@ export const languageSpanish = { "doNotSummarizeUserMessageLabel": "No Resumir Mensajes del Usuario", }, "hypaV3Modal": { - "titleLabel": "Datos de HypaV3", + "titleLabel": "HypaV3", "resetConfirmMessage": "Esta acción no se puede deshacer. ¿Desea restablecer los datos de HypaV3?", "resetConfirmSecondMessage": "Esta acción es irreversible. ¿Está realmente seguro de querer restablecer los datos de HypaV3?", "convertLabel": "Aún no hay resúmenes, pero puede convertir datos de HypaV2 a V3.", diff --git a/src/lang/ko.ts b/src/lang/ko.ts index b100c8d4..f409e77c 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -784,7 +784,7 @@ export const languageKorean = { "doNotSummarizeUserMessageLabel": "유저 메시지 요약하지 않기", }, "hypaV3Modal": { - "titleLabel": "HypaV3 데이터", + "titleLabel": "HypaV3", "resetConfirmMessage": "이 작업은 되돌릴 수 없습니다. HypaV3 데이터를 초기화하시겠습니까?", "resetConfirmSecondMessage": "이 작업은 복구할 수 없습니다. 정말로, 정말로 HypaV3 데이터를 초기화하시겠습니까?", "convertLabel": "아직 요약이 없지만, HypaV2 데이터를 V3로 변환할 수 있습니다.", diff --git a/src/lang/vi.ts b/src/lang/vi.ts index c9765433..258c9ae1 100644 --- a/src/lang/vi.ts +++ b/src/lang/vi.ts @@ -426,7 +426,7 @@ export const LanguageVietnamese = { "doNotSummarizeUserMessageLabel": "Không Tóm tắt Tin nhắn Người dùng", }, "hypaV3Modal": { - "titleLabel": "Dữ liệu HypaV3", + "titleLabel": "HypaV3", "resetConfirmMessage": "Hành động này không thể hoàn tác. Bạn có muốn đặt lại dữ liệu HypaV3 không?", "resetConfirmSecondMessage": "Hành động này không thể khôi phục. Bạn có thực sự chắc chắn muốn đặt lại dữ liệu HypaV3 không?", "convertLabel": "Chưa có tóm tắt nào, nhưng bạn có thể chuyển đổi dữ liệu HypaV2 sang V3.", From 3e3511760e5fcb96a18e69d9d2bec7c069cd2454 Mon Sep 17 00:00:00 2001 From: Bo26fhmC5M <88071760+Bo26fhmC5M@users.noreply.github.com> Date: Sun, 9 Feb 2025 15:23:24 +0900 Subject: [PATCH 14/14] feat: add import summary filtering and improve search in HypaV3 - Add important summary filtering button - Improve search navigation with focus-based results - Add mobile-friendly previous/next search buttons --- src/lang/cn.ts | 2 +- src/lang/en.ts | 2 +- src/lang/es.ts | 2 +- src/lang/ko.ts | 2 +- src/lang/vi.ts | 2 +- src/lang/zh-Hant.ts | 2 +- src/lib/Others/HypaV3Modal.svelte | 834 +++++++++++++++++------------- 7 files changed, 488 insertions(+), 358 deletions(-) diff --git a/src/lang/cn.ts b/src/lang/cn.ts index 6e543d76..abcd8dc2 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -796,7 +796,7 @@ export const languageChinese = { "convertSuccessMessage": "成功将 HypaV2 数据转换为 V3", "convertErrorMessage": "将 HypaV2 数据转换为 V3 失败:{0}", "noSummariesLabel": "尚无总结", - "searchPlaceholder": "输入 #N、ID 或搜索查询", + "searchPlaceholder": "输入 #N、ID 或搜索关键词", "summaryNumberLabel": "总结 #{0}", "deleteAfterConfirmMessage": "删除此后的所有总结?", "deleteAfterConfirmSecondMessage": "此操作无法撤销。您确定吗?", diff --git a/src/lang/en.ts b/src/lang/en.ts index 760a8dcd..ea9f3133 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -867,7 +867,7 @@ export const languageEnglish = { convertSuccessMessage: "Successfully converted HypaV2 data to V3", convertErrorMessage: "Failed to convert HypaV2 data to V3: {0}", noSummariesLabel: "No summaries yet", - searchPlaceholder: "Enter #N, ID, or search query", + searchPlaceholder: "Enter #N, ID, or query", summaryNumberLabel: "Summary #{0}", deleteAfterConfirmMessage: "Delete all summaries after this one?", deleteAfterConfirmSecondMessage: "This action cannot be undone. Are you really sure?", diff --git a/src/lang/es.ts b/src/lang/es.ts index f0c5787c..193d18a4 100644 --- a/src/lang/es.ts +++ b/src/lang/es.ts @@ -705,7 +705,7 @@ export const languageSpanish = { "convertSuccessMessage": "Datos de HypaV2 convertidos exitosamente a V3", "convertErrorMessage": "Error al convertir datos de HypaV2 a V3: {0}", "noSummariesLabel": "Aún no hay resúmenes", - "searchPlaceholder": "Ingrese #N, ID o consulta de búsqueda", + "searchPlaceholder": "Ingrese #N, ID o búsqueda", "summaryNumberLabel": "Resumen #{0}", "deleteAfterConfirmMessage": "¿Eliminar todos los resúmenes después de este?", "deleteAfterConfirmSecondMessage": "Esta acción no se puede deshacer. ¿Está realmente seguro?", diff --git a/src/lang/ko.ts b/src/lang/ko.ts index f409e77c..86d456e9 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -792,7 +792,7 @@ export const languageKorean = { "convertSuccessMessage": "HypaV2 데이터를 V3로 성공적으로 변환했습니다", "convertErrorMessage": "HypaV2 데이터를 V3로 변환하는데 실패했습니다: {0}", "noSummariesLabel": "아직 요약이 없습니다", - "searchPlaceholder": "#N, ID 또는 검색어를 입력하세요", + "searchPlaceholder": "#N, ID 또는 검색어 입력", "summaryNumberLabel": "요약 #{0}", "deleteAfterConfirmMessage": "이 요약 이후의 모든 요약을 삭제하시겠습니까?", "deleteAfterConfirmSecondMessage": "이 작업은 되돌릴 수 없습니다. 정말 삭제하시겠습니까?", diff --git a/src/lang/vi.ts b/src/lang/vi.ts index 258c9ae1..2d8a7275 100644 --- a/src/lang/vi.ts +++ b/src/lang/vi.ts @@ -434,7 +434,7 @@ export const LanguageVietnamese = { "convertSuccessMessage": "Đã chuyển đổi thành công dữ liệu HypaV2 sang V3", "convertErrorMessage": "Chuyển đổi dữ liệu HypaV2 sang V3 thất bại: {0}", "noSummariesLabel": "Chưa có tóm tắt nào", - "searchPlaceholder": "Nhập #N, ID, hoặc từ khóa tìm kiếm", + "searchPlaceholder": "Nhập #N, ID hoặc từ khóa", "summaryNumberLabel": "Tóm tắt #{0}", "deleteAfterConfirmMessage": "Xóa tất cả các tóm tắt sau tóm tắt này?", "deleteAfterConfirmSecondMessage": "Hành động này không thể hoàn tác. Bạn có chắc chắn không?", diff --git a/src/lang/zh-Hant.ts b/src/lang/zh-Hant.ts index 3f580f64..6325845c 100644 --- a/src/lang/zh-Hant.ts +++ b/src/lang/zh-Hant.ts @@ -829,7 +829,7 @@ export const languageChineseTraditional = { "convertSuccessMessage": "成功將 HypaV2 數據轉換為 V3", "convertErrorMessage": "無法將 HypaV2 數據轉換為 V3:{0}", "noSummariesLabel": "尚無摘要", - "searchPlaceholder": "輸入 #N、ID 或搜索查詢", + "searchPlaceholder": "輸入 #N、ID 或搜尋關鍵字", "summaryNumberLabel": "摘要 #{0}", "deleteAfterConfirmMessage": "刪除此摘要之後的所有摘要?", "deleteAfterConfirmSecondMessage": "此操作無法撤銷。您確定要這樣做嗎?", diff --git a/src/lib/Others/HypaV3Modal.svelte b/src/lib/Others/HypaV3Modal.svelte index 03bbb14a..51451563 100644 --- a/src/lib/Others/HypaV3Modal.svelte +++ b/src/lib/Others/HypaV3Modal.svelte @@ -5,6 +5,8 @@ SettingsIcon, Trash2Icon, XIcon, + ChevronUpIcon, + ChevronDownIcon, LanguagesIcon, StarIcon, RefreshCw, @@ -51,20 +53,30 @@ translationRef: HTMLTextAreaElement; } - interface SearchResult { - element: HTMLElement; - matchType: "chatMemo" | "summary"; - summaryPosition?: { - start: number; - end: number; - }; + class SummarySearchResult { + constructor( + public summaryIndex: number, + public start: number, + public end: number + ) {} } + class ChatMemoSearchResult { + constructor( + public summaryIndex: number, + public memoIndex: number + ) {} + } + + type SearchResult = SummarySearchResult | ChatMemoSearchResult; + interface SearchUI { ref: HTMLInputElement; query: string; - currentIndex: number; results: SearchResult[]; + currentResultIndex: number; + requestedSearchFromIndex: number; + isNavigating: boolean; } const hypaV3DataState = $derived( @@ -76,6 +88,7 @@ let summaryUIStates = $state([]); let expandedMessageUIState = $state(null); let searchUIState = $state(null); + let showImportantOnly = $state(false); $effect.pre(() => { summaryUIStates = hypaV3DataState.summaries.map((summary) => ({ @@ -111,8 +124,10 @@ searchUIState = { ref: null, query: "", - currentIndex: -1, results: [], + currentResultIndex: -1, + requestedSearchFromIndex: -1, + isNavigating: false, }; // Focus on search element after it's rendered @@ -135,122 +150,187 @@ } if (e.key === "Enter") { - e.preventDefault(); // Prevent event default action + e.preventDefault?.(); // Prevent event default action const query = searchUIState.query.trim(); if (!query) return; - // Search summary index - if (query.match(/^#\d+$/)) { - const summaryNumber = parseInt(query.substring(1)) - 1; + // When received a new query + if (searchUIState.currentResultIndex === -1) { + const results = generateSearchResults(query); - if ( - summaryNumber >= 0 && - summaryNumber < hypaV3DataState.summaries.length - ) { - summaryUIStates[summaryNumber].originalRef.scrollIntoView({ - behavior: "instant", - block: "center", - }); - } - - return; - } - - const normalizedQuery = query.toLowerCase(); - - if (searchUIState.currentIndex === -1) { - const results: SearchResult[] = []; - - if (isGuidLike(query)) { - // Search chatMemo - summaryUIStates.forEach((summaryUI) => { - summaryUI.chatMemoRefs.forEach((buttonRef) => { - const buttonText = buttonRef.textContent?.toLowerCase() || ""; - - if (buttonText.includes(normalizedQuery)) { - results.push({ - element: buttonRef as HTMLButtonElement, - matchType: "chatMemo", - }); - } - }); - }); - } else { - // Search summary - summaryUIStates.forEach((summaryUI) => { - const textAreaText = summaryUI.originalRef.value?.toLowerCase(); - - let pos = -1; - while ( - (pos = textAreaText.indexOf(normalizedQuery, pos + 1)) !== -1 - ) { - results.push({ - element: summaryUI.originalRef as HTMLTextAreaElement, - matchType: "summary", - summaryPosition: { - start: pos, - end: pos + normalizedQuery.length, - }, - }); - } - }); - } + if (results.length === 0) return; searchUIState.results = results; } - if (searchUIState.results.length === 0) return; + const nextResult = getNextSearchResult(e.shiftKey); - // Move to next result - searchUIState.currentIndex = - (searchUIState.currentIndex + 1) % searchUIState.results.length; + if (nextResult) { + navigateToSearchResult(nextResult); + } + } + } - const result = searchUIState.results[searchUIState.currentIndex]; + function generateSearchResults(query: string): SearchResult[] { + const results: SearchResult[] = []; + const normalizedQuery = query.trim().toLowerCase(); + + // Search summary index + if (query.match(/^#\d+$/)) { + const summaryNumber = parseInt(query.substring(1)) - 1; + + if ( + summaryNumber >= 0 && + summaryNumber < hypaV3DataState.summaries.length && + (!showImportantOnly || + hypaV3DataState.summaries[summaryNumber].isImportant) + ) { + results.push(new SummarySearchResult(summaryNumber, 0, 0)); + } + + return results; + } + + if (isGuidLike(query)) { + // Search chatMemo + summaryUIStates.forEach((summaryUI, summaryIndex) => { + if ( + !showImportantOnly || + hypaV3DataState.summaries[summaryIndex].isImportant + ) { + summaryUI.chatMemoRefs.forEach((buttonRef, memoIndex) => { + const buttonText = buttonRef.textContent?.toLowerCase() || ""; + + if (buttonText.includes(normalizedQuery)) { + results.push(new ChatMemoSearchResult(summaryIndex, memoIndex)); + } + }); + } + }); + } else { + // Search summary + summaryUIStates.forEach((summaryUI, summaryIndex) => { + if ( + !showImportantOnly || + hypaV3DataState.summaries[summaryIndex].isImportant + ) { + const textAreaText = summaryUI.originalRef.value?.toLowerCase(); + let pos = -1; + + while ( + (pos = textAreaText.indexOf(normalizedQuery, pos + 1)) !== -1 + ) { + results.push( + new SummarySearchResult( + summaryIndex, + pos, + pos + normalizedQuery.length + ) + ); + } + } + }); + } + + return results; + } + + function isGuidLike(str: string): boolean { + const strTrimed = str.trim(); + + // Exclude too short inputs + if (strTrimed.length < 4) return false; + + return /^[0-9a-f]{4,12}(-[0-9a-f]{4,12}){0,4}-?$/i.test(strTrimed); + } + + function getNextSearchResult(backward: boolean): SearchResult | null { + if (!searchUIState || searchUIState.results.length === 0) return null; + + let nextIndex: number; + + if (searchUIState.requestedSearchFromIndex !== -1) { + const fromSummaryIndex = searchUIState.requestedSearchFromIndex; + + nextIndex = backward + ? searchUIState.results.findLastIndex( + (r) => r.summaryIndex <= fromSummaryIndex + ) + : searchUIState.results.findIndex( + (r) => r.summaryIndex >= fromSummaryIndex + ); + + if (nextIndex === -1) { + nextIndex = backward ? searchUIState.results.length - 1 : 0; + } + + searchUIState.requestedSearchFromIndex = -1; + } else { + const delta = backward ? -1 : 1; + + nextIndex = + (searchUIState.currentResultIndex + + delta + + searchUIState.results.length) % + searchUIState.results.length; + } + + searchUIState.currentResultIndex = nextIndex; + return searchUIState.results[nextIndex]; + } + + function navigateToSearchResult(result: SearchResult) { + searchUIState.isNavigating = true; + + if (result instanceof SummarySearchResult) { + const textarea = summaryUIStates[result.summaryIndex].originalRef; // Scroll to element - result.element.scrollIntoView({ + textarea.scrollIntoView({ behavior: "instant", block: "center", }); - if (result.matchType === "chatMemo") { - // Highlight chatMemo result - result.element.classList.add("ring-2", "ring-zinc-500"); + if (result.start === result.end) { + return; + } - // Remove highlight after a short delay - window.setTimeout(() => { - result.element.classList.remove("ring-2", "ring-zinc-500"); - }, 1000); - } else { - // Handle summary text selection - const textarea = result.element as HTMLTextAreaElement; + // Scroll to query + textarea.setSelectionRange(result.start, result.end); + scrollToSelection(textarea); + // Highlight query on desktop environment + if (!("ontouchend" in window)) { // Make readonly temporarily textarea.readOnly = true; - - // Select query - textarea.setSelectionRange( - result.summaryPosition.start, - result.summaryPosition.end - ); - textarea.focus(); - scrollToSelection(textarea); - - // This only works on firefox - //textarea.scrollTop = textarea.scrollHeight; // Scroll to the bottom - //textarea.blur(); // Collapse selection - //textarea.focus(); // This scrolls the textarea - - // Highlight textarea window.setTimeout(() => { searchUIState.ref.focus(); // Restore focus to search bar textarea.readOnly = false; // Remove readonly after focus moved }, 300); } + } else { + const button = + summaryUIStates[result.summaryIndex].chatMemoRefs[result.memoIndex]; + + // Scroll to element + button.scrollIntoView({ + behavior: "instant", + block: "center", + }); + + // Highlight chatMemo + button.classList.add("ring-2", "ring-zinc-500"); + + // Remove highlight after a short delay + window.setTimeout(() => { + button.classList.remove("ring-2", "ring-zinc-500"); + }, 1000); } + + searchUIState.isNavigating = false; } function scrollToSelection(textarea: HTMLTextAreaElement) { @@ -287,15 +367,6 @@ textarea.scrollTop = selectionTop - textarea.clientHeight / 2; } - function isGuidLike(str: string): boolean { - const strTrimed = str.trim(); - - // Exclude too short inputs - if (strTrimed.length < 4) return false; - - return /^[0-9a-f]{4,12}(-[0-9a-f]{4,12}){0,4}-?$/i.test(strTrimed); - } - async function toggleTranslate( summaryIndex: number, regenerate?: boolean @@ -767,14 +838,35 @@ + + +
-
+
{#if hypaV3DataState.summaries.length === 0} {#if isHypaV2ConversionPossible()} @@ -841,6 +935,7 @@
+ + + + +
@@ -918,276 +1029,295 @@ {#each hypaV3DataState.summaries as summary, i} - {#if summaryUIStates[i]} - -
- -
- {language.hypaV3Modal.summaryNumberLabel.replace( - "{0}", - (i + 1).toString() - )} - -
- - - - - - - - - - - -
-
- - -
- -
- - - {#if summaryUIStates[i].translation} -
-
- {language.hypaV3Modal.translationLabel} -
- - -
- {/if} - - {#if summaryUIStates[i].rerolledText} - -
-
- {language.hypaV3Modal.rerolledSummaryLabel} -
- - - - - - - - -
-
-
- - -
- -
- - - {#if summaryUIStates[i].rerolledTranslation} -
-
- {language.hypaV3Modal.rerolledTranslationLabel} -
- - -
- {/if} - {/if} - - -
+ {#if !showImportantOnly || summary.isImportant} + {#if summaryUIStates[i]} + +
+
{language.hypaV3Modal.connectedMessageCountLabel.replace( + >{language.hypaV3Modal.summaryNumberLabel.replace( "{0}", - summary.chatMemos.length.toString() + (i + 1).toString() )}
- + + + + + + + + + +
-
- -
- {#each summary.chatMemos as chatMemo, memoIndex} - - {/each} -
- - {#if expandedMessageUIState?.summaryIndex === i} - +
- - {#await getProcessedMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage} - {#if expandedMessage} - -
- {language.hypaV3Modal.connectedMessageRoleLabel.replace( - "{0}", - expandedMessage.role - )} -
- - - - {:else} - {language.hypaV3Modal - .connectedMessageNotFoundLabel} - {/if} - {:catch error} - {language.hypaV3Modal.connectedMessageLoadingError.replace( - "{0}", - error.message - )} - {/await} +
- - {#if expandedMessageUIState.translation} + + {#if summaryUIStates[i].translation}
- {language.hypaV3Modal.connectedMessageTranslationLabel} + {language.hypaV3Modal.translationLabel}
{/if} - {/if} -
+ + {#if summaryUIStates[i].rerolledText} + +
+
+ {language.hypaV3Modal.rerolledSummaryLabel} +
+ + + + + + + + +
+
+
+ + +
+ +
+ + + {#if summaryUIStates[i].rerolledTranslation} +
+
+ {language.hypaV3Modal.rerolledTranslationLabel} +
+ + +
+ {/if} + {/if} + + +
+
+ {language.hypaV3Modal.connectedMessageCountLabel.replace( + "{0}", + summary.chatMemos.length.toString() + )} + +
+ + +
+
+
+ + +
+ {#each summary.chatMemos as chatMemo, memoIndex} + + {/each} +
+ + {#if expandedMessageUIState?.summaryIndex === i} + +
+ + {#await getProcessedMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage} + {#if expandedMessage} + +
+ {language.hypaV3Modal.connectedMessageRoleLabel.replace( + "{0}", + expandedMessage.role + )} +
+ + + + {:else} + {language.hypaV3Modal + .connectedMessageNotFoundLabel} + {/if} + {:catch error} + {language.hypaV3Modal.connectedMessageLoadingError.replace( + "{0}", + error.message + )} + {/await} +
+ + + {#if expandedMessageUIState.translation} +
+
+ {language.hypaV3Modal.connectedMessageTranslationLabel} +
+ + +
+ {/if} + {/if} +
+ {/if} {/if} {/each} @@ -1209,8 +1339,8 @@
{:else}