fix: improve HypaV3 Modal (#851)

# PR Checklist
- [ ] Have you checked if it works normally in all models? *Ignore this
if it doesn't use models.*
- [ ] Have you checked if it works normally in all web, local, and node
hosted versions? If it doesn't, have you blocked it in those versions?
- [ ] Have you added type definitions?

# Description
This PR introduces following:
- fix: pass message index when processing regex script in HypaV3 Modal
- feat: add BGE-m3-ko embedding
This commit is contained in:
kwaroran
2025-05-18 17:15:48 +09:00
committed by GitHub
6 changed files with 86 additions and 70 deletions

View File

@@ -413,11 +413,17 @@
} }
function isOrphan(summaryIndex: number): boolean { function isOrphan(summaryIndex: number): boolean {
const char = DBState.db.characters[$selectedCharID];
const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage];
const summary = hypaV3DataState.summaries[summaryIndex]; const summary = hypaV3DataState.summaries[summaryIndex];
for (const chatMemo of summary.chatMemos) { for (const chatMemo of summary.chatMemos) {
if (!getMessageFromChatMemo(chatMemo)) { if (chatMemo == null) {
return true; // Check first message exists
if (!getFirstMessage()) return true;
} else {
if (chat.message.findIndex((m) => m.chatId === chatMemo) === -1)
return true;
} }
} }
@@ -437,8 +443,7 @@
const summary = hypaV3DataState.summaries[summaryIndex]; const summary = hypaV3DataState.summaries[summaryIndex];
const toSummarize: OpenAIChat[] = await Promise.all( const toSummarize: OpenAIChat[] = await Promise.all(
summary.chatMemos.map(async (chatMemo) => { summary.chatMemos.map(async (chatMemo) => {
// Processed message const message = await getMessageFromChatMemo(chatMemo);
const message = await getProcessedMessageFromChatMemo(chatMemo);
return { return {
role: (message.role === "char" role: (message.role === "char"
@@ -495,31 +500,28 @@
summaryUIState.isRerolledTranslating = false; summaryUIState.isRerolledTranslating = false;
} }
async function getProcessedMessageFromChatMemo( async function getMessageFromChatMemo(
chatMemo: string | null chatMemo: string | null
): Promise<Message | null> { ): Promise<Message | null> {
const unprocessed = getMessageFromChatMemo(chatMemo);
if (!unprocessed) {
return null;
}
return getCurrentHypaV3Preset().settings.processRegexScript
? await processRegexScript(unprocessed)
: unprocessed;
}
function getMessageFromChatMemo(chatMemo: string | null): Message | null {
const char = DBState.db.characters[$selectedCharID]; const char = DBState.db.characters[$selectedCharID];
const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage]; const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage];
const shouldProcess = getCurrentHypaV3Preset().settings.processRegexScript;
let msg = null;
let msgIndex = -1;
if (chatMemo == null) { if (chatMemo == null) {
const firstMessage = getFirstMessage(); const firstMessage = getFirstMessage();
return firstMessage ? { role: "char", data: firstMessage } : null; if (!firstMessage) return null;
msg = { role: "char", data: firstMessage };
} else {
msgIndex = chat.message.findIndex((m) => m.chatId === chatMemo);
if (msgIndex === -1) return null;
msg = chat.message[msgIndex];
} }
return chat.message.find((m) => m.chatId === chatMemo) || null; return shouldProcess ? await processRegexScript(msg, msgIndex) : msg;
} }
function getFirstMessage(): string | null { function getFirstMessage(): string | null {
@@ -533,15 +535,17 @@
: null; : null;
} }
async function processRegexScript(msg: Message): Promise<Message> { async function processRegexScript(
msg: Message,
msgIndex: number = -1
): Promise<Message> {
const char = DBState.db.characters[$selectedCharID]; const char = DBState.db.characters[$selectedCharID];
const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage];
const newData: string = ( const newData: string = (
await processScriptFull( await processScriptFull(
char, char,
risuChatParser(msg.data, { chara: char, role: msg.role }), risuChatParser(msg.data, { chara: char, role: msg.role }),
"editprocess", "editprocess",
-1, msgIndex,
{ {
chatRole: msg.role, chatRole: msg.role,
} }
@@ -593,8 +597,7 @@
return; return;
} }
// Processed message const message = await getMessageFromChatMemo(
const message = await getProcessedMessageFromChatMemo(
expandedMessageUIState.selectedChatMemo expandedMessageUIState.selectedChatMemo
); );
@@ -632,52 +635,41 @@
} }
} }
async function getProcessedNextSummarizationTarget(): Promise<Message | null> { async function getNextSummarizationTarget(): Promise<Message | null> {
const unprocessed = getNextSummarizationTarget();
if (!unprocessed) {
return null;
}
return getCurrentHypaV3Preset().settings.processRegexScript
? await processRegexScript(unprocessed)
: unprocessed;
}
function getNextSummarizationTarget(): Message | null {
const char = DBState.db.characters[$selectedCharID]; const char = DBState.db.characters[$selectedCharID];
const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage]; const chat = char.chats[DBState.db.characters[$selectedCharID].chatPage];
const shouldProcess = getCurrentHypaV3Preset().settings.processRegexScript;
// Summaries exist // Summaries exist
if (hypaV3DataState.summaries.length > 0) { if (hypaV3DataState.summaries.length > 0) {
const lastSummary = hypaV3DataState.summaries.at(-1); const lastSummary = hypaV3DataState.summaries.at(-1);
const lastMessageIndex = chat.message.findIndex( const lastMessageIndex = chat.message.findIndex(
(msg) => msg.chatId === lastSummary.chatMemos.at(-1) (m) => m.chatId === lastSummary.chatMemos.at(-1)
); );
if (lastMessageIndex !== -1) { if (lastMessageIndex !== -1) {
const nextMessage = chat.message[lastMessageIndex + 1]; const next = chat.message[lastMessageIndex + 1] ?? null;
if (nextMessage) { return next && shouldProcess
return nextMessage; ? await processRegexScript(next, lastMessageIndex + 1)
} : next;
} }
} }
// When no summaries exist OR couldn't find last connected message,
// check if first message is available
const firstMessage = getFirstMessage(); const firstMessage = getFirstMessage();
// When no summaries exist OR couldn't find last connected message if (!firstMessage) {
// Check if first message is available const next = chat.message[0] ?? null;
if (!firstMessage || firstMessage.trim() === "") {
if (chat.message.length > 0) {
return chat.message[0];
}
return null; return next && shouldProcess ? await processRegexScript(next, 0) : next;
} }
// will summarize first message // Will summarize first message
return { role: "char", chatId: "first", data: firstMessage }; const next: Message = { role: "char", chatId: "first", data: firstMessage };
return shouldProcess ? await processRegexScript(next) : next;
} }
function isHypaV2ConversionPossible(): boolean { function isHypaV2ConversionPossible(): boolean {
@@ -1278,8 +1270,7 @@
{#if expandedMessageUIState?.summaryIndex === i} {#if expandedMessageUIState?.summaryIndex === i}
<!-- Expanded Message --> <!-- Expanded Message -->
<div class="mt-2 sm:mt-4"> <div class="mt-2 sm:mt-4">
<!-- Processed Message --> {#await getMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage}
{#await getProcessedMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage}
{#if expandedMessage} {#if expandedMessage}
<!-- Role --> <!-- Role -->
<div class="mb-2 sm:mb-4 text-sm text-zinc-400"> <div class="mb-2 sm:mb-4 text-sm text-zinc-400">
@@ -1336,7 +1327,7 @@
<!-- Next Summarization Target --> <!-- Next Summarization Target -->
<div class="mt-2 sm:mt-4"> <div class="mt-2 sm:mt-4">
{#await getProcessedNextSummarizationTarget() then nextMessage} {#await getNextSummarizationTarget() then nextMessage}
{#if nextMessage} {#if nextMessage}
{@const chatId = {@const chatId =
nextMessage.chatId === "first" nextMessage.chatId === "first"

View File

@@ -5,6 +5,7 @@
import SelectInput from "../UI/GUI/SelectInput.svelte"; import SelectInput from "../UI/GUI/SelectInput.svelte";
import Button from "../UI/GUI/Button.svelte"; import Button from "../UI/GUI/Button.svelte";
import { HypaProcesser } from "src/ts/process/memory/hypamemory"; import { HypaProcesser } from "src/ts/process/memory/hypamemory";
import { DBState } from "src/ts/stores.svelte"
let query = $state(""); let query = $state("");
let model = $state("MiniLM"); let model = $state("MiniLM");
@@ -27,24 +28,43 @@
<h2 class="text-4xl text-textcolor my-6 font-black relative">{language.embedding}</h2> <h2 class="text-4xl text-textcolor my-6 font-black relative">{language.embedding}</h2>
<span class="text-textcolor text-lg">Model</span> <span class="text-textcolor text-lg">Model</span>
<SelectInput bind:value={model}> <SelectInput bind:value={model} className="mb-4">
{#if 'gpu' in navigator}
<OptionInput value="MiniLMGPU">MiniLM L6 v2 (GPU)</OptionInput>
<OptionInput value="nomicGPU">Nomic Embed Text v1.5 (GPU)</OptionInput>
<OptionInput value="bgeSmallEnGPU">BGE Small English (GPU)</OptionInput>
<OptionInput value="bgem3GPU">BGE Medium 3 (GPU)</OptionInput>
<OptionInput value="multiMiniLMGPU">Multilingual MiniLM L12 v2 (GPU)</OptionInput>
<OptionInput value="bgeM3KoGPU">BGE Medium 3 Korean (GPU)</OptionInput>
{/if}
<OptionInput value="MiniLM">MiniLM L6 v2 (CPU)</OptionInput> <OptionInput value="MiniLM">MiniLM L6 v2 (CPU)</OptionInput>
<OptionInput value="nomic">Nomic Embed Text v1.5 (CPU)</OptionInput> <OptionInput value="nomic">Nomic Embed Text v1.5 (CPU)</OptionInput>
<OptionInput value="nomicGPU">Nomic Embed Text v1.5 (GPU)</OptionInput>
<OptionInput value="bgeSmallEn">BGE Small English (CPU)</OptionInput> <OptionInput value="bgeSmallEn">BGE Small English (CPU)</OptionInput>
<OptionInput value="bgeSmallEnGPU">BGE Small English (GPU)</OptionInput>
<OptionInput value="bgem3">BGE Medium 3 (CPU)</OptionInput> <OptionInput value="bgem3">BGE Medium 3 (CPU)</OptionInput>
<OptionInput value="bgem3GPU">BGE Medium 3 (GPU)</OptionInput> <OptionInput value="multiMiniLM">Multilingual MiniLM L12 v2 (CPU)</OptionInput>
<OptionInput value="bgeM3Ko">BGE Medium 3 Korean (CPU)</OptionInput>
<OptionInput value="openai3small">OpenAI text-embedding-3-small</OptionInput> <OptionInput value="openai3small">OpenAI text-embedding-3-small</OptionInput>
<OptionInput value="openai3large">OpenAI text-embedding-3-large</OptionInput> <OptionInput value="openai3large">OpenAI text-embedding-3-large</OptionInput>
<OptionInput value="ada">OpenAI Ada</OptionInput>
<OptionInput value="custom">Custom (OpenAI-compatible)</OptionInput> <OptionInput value="custom">Custom (OpenAI-compatible)</OptionInput>
</SelectInput> </SelectInput>
{#if model === "custom"} {#if model === 'openai3small' || model === 'openai3large' || model === 'ada'}
<span class="text-textcolor text-lg">Custom Server URL</span> <span class="text-textcolor text-lg">OpenAI API Key</span>
<TextInput bind:value={customEmbeddingUrl} size="lg" fullwidth /> <TextInput size="sm" marginBottom bind:value={DBState.db.supaMemoryKey}/>
{/if} {/if}
{#if model === "custom"}
<span class="text-textcolor text-lg">URL</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.url}/>
<span class="text-textcolor text-lg">Key/Password</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.key}/>
<span class="text-textcolor text-lg">Request Model</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.model}/>
{/if}
<div class="mb-4"></div>
<span class="text-textcolor text-lg">Query</span> <span class="text-textcolor text-lg">Query</span>
<TextInput bind:value={query} size="lg" fullwidth /> <TextInput bind:value={query} size="lg" fullwidth />

View File

@@ -917,19 +917,21 @@
{/if} {/if}
<span class="text-textcolor">{language.embedding}</span> <span class="text-textcolor">{language.embedding}</span>
<SelectInput className="mt-2 mb-2" bind:value={DBState.db.hypaModel}> <SelectInput className="mb-4" bind:value={DBState.db.hypaModel}>
{#if 'gpu' in navigator} {#if 'gpu' in navigator}
<OptionInput value="MiniLMGPU">MiniLM L6 v2 (GPU)</OptionInput> <OptionInput value="MiniLMGPU">MiniLM L6 v2 (GPU)</OptionInput>
<OptionInput value="nomicGPU">Nomic Embed Text v1.5 (GPU)</OptionInput> <OptionInput value="nomicGPU">Nomic Embed Text v1.5 (GPU)</OptionInput>
<OptionInput value="bgeSmallEnGPU">BGE Small English (GPU)</OptionInput> <OptionInput value="bgeSmallEnGPU">BGE Small English (GPU)</OptionInput>
<OptionInput value="bgem3GPU">BGE Medium 3 (GPU)</OptionInput> <OptionInput value="bgem3GPU">BGE Medium 3 (GPU)</OptionInput>
<OptionInput value="multiMiniLMGPU">Multilingual MiniLM L12 v2 (GPU)</OptionInput> <OptionInput value="multiMiniLMGPU">Multilingual MiniLM L12 v2 (GPU)</OptionInput>
<OptionInput value="bgeM3KoGPU">BGE Medium 3 Korean (GPU)</OptionInput>
{/if} {/if}
<OptionInput value="MiniLM">MiniLM L6 v2 (CPU)</OptionInput> <OptionInput value="MiniLM">MiniLM L6 v2 (CPU)</OptionInput>
<OptionInput value="nomic">Nomic Embed Text v1.5 (CPU)</OptionInput> <OptionInput value="nomic">Nomic Embed Text v1.5 (CPU)</OptionInput>
<OptionInput value="bgeSmallEn">BGE Small English (CPU)</OptionInput> <OptionInput value="bgeSmallEn">BGE Small English (CPU)</OptionInput>
<OptionInput value="bgem3">BGE Medium 3 (CPU)</OptionInput> <OptionInput value="bgem3">BGE Medium 3 (CPU)</OptionInput>
<OptionInput value="multiMiniLM">Multilingual MiniLM L12 v2 (CPU)</OptionInput> <OptionInput value="multiMiniLM">Multilingual MiniLM L12 v2 (CPU)</OptionInput>
<OptionInput value="bgeM3Ko">BGE Medium 3 Korean (CPU)</OptionInput>
<OptionInput value="openai3small">OpenAI text-embedding-3-small</OptionInput> <OptionInput value="openai3small">OpenAI text-embedding-3-small</OptionInput>
<OptionInput value="openai3large">OpenAI text-embedding-3-large</OptionInput> <OptionInput value="openai3large">OpenAI text-embedding-3-large</OptionInput>
<OptionInput value="ada">OpenAI Ada</OptionInput> <OptionInput value="ada">OpenAI Ada</OptionInput>
@@ -942,12 +944,12 @@
{/if} {/if}
{#if DBState.db.hypaModel === 'custom'} {#if DBState.db.hypaModel === 'custom'}
<span class="text-textcolor">URL</span> <span class="text-textcolor">URL</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.url}/> <TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.url}/>
<span class="text-textcolor">Key/Password</span> <span class="text-textcolor">Key/Password</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.key}/> <TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.key}/>
<span class="text-textcolor">Request Model</span> <span class="text-textcolor">Request Model</span>
<TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.model}/> <TextInput size="sm" marginBottom bind:value={DBState.db.hypaCustomSettings.model}/>
{/if} {/if}
</Arcodion> </Arcodion>

View File

@@ -21,6 +21,8 @@ export const localModels = {
'bgem3GPU': 'Xenova/bge-m3', 'bgem3GPU': 'Xenova/bge-m3',
'multiMiniLM': 'Xenova/paraphrase-multilingual-MiniLM-L12-v2', 'multiMiniLM': 'Xenova/paraphrase-multilingual-MiniLM-L12-v2',
'multiMiniLMGPU': 'Xenova/paraphrase-multilingual-MiniLM-L12-v2', 'multiMiniLMGPU': 'Xenova/paraphrase-multilingual-MiniLM-L12-v2',
'bgeM3Ko': 'HyperBlaze/BGE-m3-ko',
'bgeM3KoGPU': 'HyperBlaze/BGE-m3-ko',
}, },
gpuModels:[ gpuModels:[
'MiniLMGPU', 'MiniLMGPU',
@@ -28,6 +30,7 @@ export const localModels = {
'bgeSmallEnGPU', 'bgeSmallEnGPU',
'bgem3GPU', 'bgem3GPU',
'multiMiniLMGPU', 'multiMiniLMGPU',
'bgeM3KoGPU',
] ]
} }

View File

@@ -128,7 +128,7 @@ export async function hypaMemoryV3(
} finally { } finally {
if (settings.summarizationModel !== "subModel") { if (settings.summarizationModel !== "subModel") {
try { try {
unloadEngine(); await unloadEngine();
} catch {} } catch {}
} }
} }

View File

@@ -66,7 +66,7 @@ export const runEmbedding = async (texts: string[], model:EmbeddingModel = 'Xeno
} }
extractor = await pipeline('feature-extraction', model, { extractor = await pipeline('feature-extraction', model, {
// Default dtype for webgpu is fp32, so we can use q8, which is the default dtype in wasm. // Default dtype for webgpu is fp32, so we can use q8, which is the default dtype in wasm.
...(device === 'webgpu' ? { dtype: "q8" } : {}), dtype: "q8",
device: device, device: device,
progress_callback: (progress) => { progress_callback: (progress) => {
console.log(progress) console.log(progress)