From d400109a8520eb6e1a9c479bd9fdcfb2609d04e7 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Mon, 10 Mar 2025 19:37:35 +0900 Subject: [PATCH 1/7] Add compressions --- src/ts/process/coldstorage.svelte.ts | 89 ++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/src/ts/process/coldstorage.svelte.ts b/src/ts/process/coldstorage.svelte.ts index ad961383..2bb6dcc5 100644 --- a/src/ts/process/coldstorage.svelte.ts +++ b/src/ts/process/coldstorage.svelte.ts @@ -7,17 +7,50 @@ import { readDir, remove } from "@tauri-apps/plugin-fs" -import { isTauri } from "../globalApi.svelte" +import { forageStorage, isTauri } from "../globalApi.svelte" import { DBState } from "../stores.svelte" +import { hubURL } from "../characterCards" +import type { AccountStorage } from "../storage/accountStorage" const coldStorageHeader = '\uEF01COLDSTORAGE\uEF01' +async function decompress(data:Uint8Array) { + const fflate = await import('fflate') + return new Promise((resolve, reject) => { + fflate.decompress(data, (err, decompressed) => { + if (err) { + reject(err) + } + resolve(decompressed) + }) + }) +} + async function getColdStorageItem(key:string) { - - if(isTauri){ + + if(forageStorage.isAccount){ + const d = await fetch(hubURL + '/hub/account/coldstorage', { + method: 'GET', + headers: { + 'x-risu-key': key, + 'x-risu-auth': (forageStorage.realStorage as AccountStorage).auth + } + }) + + if(d.status === 200){ + const buf = await d.arrayBuffer() + const text = new TextDecoder().decode(await decompress(new Uint8Array(buf))) + return JSON.parse(text) + } + return null + } + + else if(isTauri){ try { - const f = await readFile('./coldstorage/'+key+'.json') - const text = new TextDecoder().decode(f) + const f = await readFile('./coldstorage/'+key+'.json', { + baseDir: BaseDirectory.AppData + }) + const text = new TextDecoder().decode(await decompress(new Uint8Array(f))) return JSON.parse(text) } catch (error) { return null @@ -36,7 +69,7 @@ async function getColdStorageItem(key:string) { return null } const buf = await d.arrayBuffer() - const text = new TextDecoder().decode(buf) + const text = new TextDecoder().decode(await decompress(new Uint8Array(buf))) return JSON.parse(text) } catch (error) { return null @@ -45,13 +78,42 @@ async function getColdStorageItem(key:string) { } async function setColdStorageItem(key:string, value:any) { - if(isTauri){ + + const fflate = await import('fflate') + const json = JSON.stringify(value) + const compressed = await (new Promise((resolve, reject) => { + fflate.compress(new TextEncoder().encode(json), (err, compressed) => { + if (err) { + reject(err) + } + resolve(compressed) + }) + })) + + if(forageStorage.isAccount){ + const res = await fetch(hubURL + '/hub/account/coldstorage', { + method: 'POST', + headers: { + 'x-risu-key': key, + 'x-risu-auth': (forageStorage.realStorage as AccountStorage).auth, + 'content-type': 'application/json' + }, + body: compressed + }) + if(res.status !== 200){ + try { + console.error('Error setting cold storage item') + console.error(await res.text()) + } catch (error) {} + } + return + } + else if(isTauri){ try { if(!(await exists('./coldstorage'))){ - await mkdir('./coldstorage', { recursive: true }) + await mkdir('./coldstorage', { recursive: true, baseDir: BaseDirectory.AppData }) } - const text = JSON.stringify(value) - await writeFile('./coldstorage/'+key+'.json', new TextEncoder().encode(text)) + await writeFile('./coldstorage/'+key+'.json', compressed, { baseDir: BaseDirectory.AppData }) } catch (error) { console.error(error) } @@ -62,8 +124,7 @@ async function setColdStorageItem(key:string, value:any) { const opfs = await navigator.storage.getDirectory() const file = await opfs.getFileHandle('coldstorage_' + key+'.json', { create: true }) const writable = await file.createWritable() - const text = JSON.stringify(value) - await writable.write(new TextEncoder().encode(text)) + await writable.write(compressed) await writable.close() } catch (error) { console.error(error) @@ -92,6 +153,10 @@ async function removeColdStorageItem(key:string) { export async function makeColdData(){ + if(!DBState.db.chatCompression){ + return + } + const currentTime = Date.now() const coldTime = currentTime - 1000 * 60 * 60 * 24 * 30 //30 days before now From e2c92ed473a07d6401633b75b603c240e1dd5d80 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Mon, 10 Mar 2025 19:38:55 +0900 Subject: [PATCH 2/7] Add conditional rendering for chat compression based on server type --- src/lib/Setting/Pages/AdvancedSettings.svelte | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/Setting/Pages/AdvancedSettings.svelte b/src/lib/Setting/Pages/AdvancedSettings.svelte index 42afafd7..021c3f25 100644 --- a/src/lib/Setting/Pages/AdvancedSettings.svelte +++ b/src/lib/Setting/Pages/AdvancedSettings.svelte @@ -4,7 +4,7 @@ import Button from "src/lib/UI/GUI/Button.svelte"; import { DBState } from 'src/ts/stores.svelte'; import { alertMd } from "src/ts/alert"; - import { getRequestLog, isTauri } from "src/ts/globalApi.svelte"; + import { getRequestLog, isNodeServer, isTauri } from "src/ts/globalApi.svelte"; import NumberInput from "src/lib/UI/GUI/NumberInput.svelte"; import TextInput from "src/lib/UI/GUI/TextInput.svelte"; import SelectInput from "src/lib/UI/GUI/SelectInput.svelte"; @@ -165,11 +165,14 @@ -
- - - -
+ + {#if !isNodeServer} +
+ + + +
+ {/if} {/if} {#if DBState.db.showUnrecommended}
From 823606f825e0d7d41349e3c90ad00d298a26ef6d Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Mon, 10 Mar 2025 19:39:12 +0900 Subject: [PATCH 3/7] Bump version to 153.0.0 in configuration and related files --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.svelte.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index dacdabb9..f5b49223 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "152.0.1", + "version": "153.0.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index f0af181d..a83673ad 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -12,7 +12,7 @@ import { defaultColorScheme, type ColorScheme } from '../gui/colorscheme'; import type { PromptItem, PromptSettings } from '../process/prompt'; import type { OobaChatCompletionRequestParams } from '../model/ooba'; -export let appVer = "152.0.1" +export let appVer = "153.0.0" export let webAppSubVer = '' diff --git a/version.json b/version.json index 91edd96a..5cb2af7e 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"152.0.1"} \ No newline at end of file +{"version":"153.0.0"} \ No newline at end of file From 1a3c111830413e6795dc9d012489a383b33d4316 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Mon, 10 Mar 2025 19:42:45 +0900 Subject: [PATCH 4/7] Fix help keys --- src/lang/en.ts | 6 +++--- src/lib/Setting/Pages/AdvancedSettings.svelte | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 147bdd81..8a353120 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -174,6 +174,8 @@ export const languageEnglish = { 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.", + automaticCachePoint: "Automatically creates cache point after the chat ends, if the caching point doesn't exist.", + experimentalChatCompressionDesc: "Compresses the unused chat data and saves in seperate file.", }, setup: { chooseProvider: "Choose AI Provider", @@ -1068,7 +1070,5 @@ export const languageEnglish = { claudeCachingRetrival: "Claude Caching Retrival", claudeCachingRetrivalDesc: "Extends the cache time for Claude Caching, by requesting every 4 minutes. this can reduce the cache miss rate, but it can increase the cost if its not used properly.", automaticCachePoint: "Automatic Cache Point", - automaticCachePointDesc: "Automatically creates cache point after the chat ends, if the caching point doesn't exist.", - experimentalChatCompression: "Experimental Chat Data Handling", - experimentalChatCompressionDesc: "Compresses the unused chat data and saves in seperate file.", + experimentalChatCompression: "Experimental Chat Data Handling" } diff --git a/src/lib/Setting/Pages/AdvancedSettings.svelte b/src/lib/Setting/Pages/AdvancedSettings.svelte index 021c3f25..fb25b726 100644 --- a/src/lib/Setting/Pages/AdvancedSettings.svelte +++ b/src/lib/Setting/Pages/AdvancedSettings.svelte @@ -162,14 +162,14 @@
- +
{#if !isNodeServer}
- +
{/if} From 40bf90a9df509f92cfbc4a1c8d90678d489937e4 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Mon, 10 Mar 2025 22:24:11 +0900 Subject: [PATCH 5/7] fix Loading chat data --- src/lang/en.ts | 5 ++-- src/lib/ChatScreens/DefaultChatScreen.svelte | 13 +++++++++++ src/lib/SideBars/SideChatList.svelte | 24 ++++---------------- src/ts/process/coldstorage.svelte.ts | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 8a353120..9ca9dbdc 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -175,7 +175,7 @@ export const languageEnglish = { 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.", automaticCachePoint: "Automatically creates cache point after the chat ends, if the caching point doesn't exist.", - experimentalChatCompressionDesc: "Compresses the unused chat data and saves in seperate file.", + experimentalChatCompressionDesc: "Compresses the unused chat data and saves in seperate file. this greatly reduces the size of the chat data, and greatly improves the performance, however its experimental and can be unstable, causing issues in backup feature and more.", }, setup: { chooseProvider: "Choose AI Provider", @@ -1070,5 +1070,6 @@ export const languageEnglish = { claudeCachingRetrival: "Claude Caching Retrival", claudeCachingRetrivalDesc: "Extends the cache time for Claude Caching, by requesting every 4 minutes. this can reduce the cache miss rate, but it can increase the cost if its not used properly.", automaticCachePoint: "Automatic Cache Point", - experimentalChatCompression: "Experimental Chat Data Handling" + experimentalChatCompression: "Experimental Chat Data Handling", + loadingChatData: "Loading Chat Data", } diff --git a/src/lib/ChatScreens/DefaultChatScreen.svelte b/src/lib/ChatScreens/DefaultChatScreen.svelte index 991d9410..c6bb3834 100644 --- a/src/lib/ChatScreens/DefaultChatScreen.svelte +++ b/src/lib/ChatScreens/DefaultChatScreen.svelte @@ -28,6 +28,7 @@ import { getInlayAsset } from 'src/ts/process/files/inlays'; import PlaygroundMenu from '../Playground/PlaygroundMenu.svelte'; import { ConnectionOpenStore } from 'src/ts/sync/multiuser'; + import { coldStorageHeader, preLoadChat } from 'src/ts/process/coldstorage.svelte'; let messageInput:string = $state('') let messageInputTranslate:string = $state('') @@ -639,6 +640,15 @@ )} {send}/> {/if} + {#if DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message?.[0]?.data?.startsWith(coldStorageHeader) } + {#await preLoadChat($selectedCharID, DBState.db.characters[$selectedCharID].chatPage)} +
+ {language.loadingChatData} +
+ {:then a} +
+ {/await} + {:else} {#each messageForm(DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message, loadPages) as chat, i} {#if chat.role === 'char'} {#if DBState.db.characters[$selectedCharID].type !== 'group'} @@ -683,6 +693,7 @@ /> {/if} {/each} + {#if DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message.length <= loadPages} {#if DBState.db.characters[$selectedCharID].type !== 'group' } { e.stopPropagation() diff --git a/src/lib/SideBars/SideChatList.svelte b/src/lib/SideBars/SideChatList.svelte index ec5da797..f3359c00 100644 --- a/src/lib/SideBars/SideChatList.svelte +++ b/src/lib/SideBars/SideChatList.svelte @@ -20,7 +20,6 @@ import { getModuleToggles } from "src/ts/process/modules"; import { language } from "src/lang"; import Toggles from "./Toggles.svelte"; - import { preLoadChat } from "src/ts/process/coldstorage.svelte"; interface Props { chara: character|groupChat; @@ -68,6 +67,7 @@ } }) + chara.chatPage = newChats.indexOf(chara.chats[currentChatPage]) chara.chats = newChats try { @@ -76,10 +76,6 @@ sorted += 1 await sleep(1) createStb() - - await preLoadChat($selectedCharID, newChats.indexOf(chara.chats[currentChatPage])) - chara.chatPage = newChats.indexOf(chara.chats[currentChatPage]) - }, ...sortableOptions })) @@ -111,17 +107,14 @@ }) chara.chatFolders = newFolders + chara.chatPage = newChats.indexOf(chara.chats[currentChatPage]) chara.chats = newChats - try { folderStb.destroy() } catch (e) {} sorted += 1 await sleep(1) createStb() - - await preLoadChat($selectedCharID, newChats.indexOf(chara.chats[currentChatPage])) - chara.chatPage = newChats.indexOf(chara.chats[currentChatPage]) }, ...sortableOptions }) @@ -251,9 +244,8 @@
{:else} {#each chara.chats.filter(chat => chat.folderId == chara.chatFolders[i].id) as chat} -