From dc524dad10796a15c2cf5b17fd024cf7265592c3 Mon Sep 17 00:00:00 2001 From: concertypin <55056558+concertypin@users.noreply.github.com> Date: Tue, 5 Aug 2025 14:06:22 +0900 Subject: [PATCH] Refactoring scripting file to dir --- .gitignore | 4 +- src/lib/ChatScreens/Chat.svelte | 1490 +++++++++++++++-------- src/ts/observer.svelte.ts | 76 +- src/ts/process/index.svelte.ts | 996 ++++++++-------- src/ts/process/script/api.ts | 695 +++++++++++ src/ts/process/script/engine/index.ts | 58 + src/ts/process/script/engine/lua.ts | 254 ++++ src/ts/process/script/engine/python.ts | 132 +++ src/ts/process/script/scriptings.ts | 106 ++ src/ts/process/script/trigger.ts | 72 ++ src/ts/process/scriptings.ts | 1290 -------------------- src/ts/process/scripts.ts | 192 +-- src/ts/process/triggers.ts | 1508 ++++++++++++------------ 13 files changed, 3691 insertions(+), 3182 deletions(-) create mode 100644 src/ts/process/script/api.ts create mode 100644 src/ts/process/script/engine/index.ts create mode 100644 src/ts/process/script/engine/lua.ts create mode 100644 src/ts/process/script/engine/python.ts create mode 100644 src/ts/process/script/scriptings.ts create mode 100644 src/ts/process/script/trigger.ts delete mode 100644 src/ts/process/scriptings.ts diff --git a/.gitignore b/.gitignore index f233cebaef..58f6973c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ lerna-debug.log* package-lock.json node_modules dist/ -save/ +save dist-web/ dist-ssr *.local @@ -48,4 +48,4 @@ dist.zip # Due to Cargo.lock makes the project build fail, we ignore it -Cargo.lock \ No newline at end of file +Cargo.lock diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index ae0bd997e2..7017cc2edc 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -1,46 +1,77 @@ - {#snippet genInfo()}
{#if messageGenerationInfo && DBState.db.requestInfoInsideChat} - {/if} - {#if DBState.db.translatorType === 'llm' && translated} - + window.navigator.clipboard + .writeText(msgDisplay) + .then(() => { + setStatusMessage(language.copied); + }); + }} + > + + {/if} {#if idx > -1} - {#if DBState.db.characters[selIdState.selId].type !== 'group' && DBState.db.characters[selIdState.selId].ttsMode !== 'none' && (DBState.db.characters[selIdState.selId].ttsMode)} - {/if} {#if !$ConnectionOpenStore} - - {/if} {/if} - {#if DBState.db.translator !== '' && !blankMessage} - {/if} {#if rerollIcon || altGreeting} {#if DBState.db.swipe || altGreeting} - {#if firstMessage && DBState.db.swipe && DBState.db.showFirstMessagePages} - {currentPage}/{totalPages} + {currentPage}/{totalPages} {/if} - {:else} - {/if} {/if}
{/snippet} -{#snippet icon(options:{rounded?:boolean,styleFix?:string} = {})} +{#snippet icon(options: { rounded?: boolean; styleFix?: string } = {})} {#if !blankMessage && !$HideIconStore} {#if DBState.db.characters[selIdState.selId]?.chaId === "§playground"} -
- {#if name === 'assistant'} +
+ {#if name === "assistant"} {:else} @@ -577,240 +891,408 @@
{:else} {#await img} -
+
{:then m} - {#if largePortrait && (!options?.rounded)} -
+ {#if largePortrait && !options?.rounded} +
{:else} -
+
{/if} {/await} {/if} {/if} {/snippet} -{#snippet renderGuiHtmlPart(dom:HTMLElement)} - {#if dom.tagName === 'IMG'} - - {:else if dom.tagName === 'A'} - +{#snippet renderGuiHtmlPart(dom: HTMLElement)} + {#if dom.tagName === "IMG"} + + {:else if dom.tagName === "A"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'SPAN'} - + {:else if dom.tagName === "SPAN"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'DIV'} -
+ {:else if dom.tagName === "DIV"} +
{@render renderChilds(dom)}
- {:else if dom.tagName === 'P'} -

+ {:else if dom.tagName === "P"} +

{@render renderChilds(dom)}

- {:else if dom.tagName === 'H1'} -

+ {:else if dom.tagName === "H1"} +

{@render renderChilds(dom)}

- {:else if dom.tagName === 'H2'} -

+ {:else if dom.tagName === "H2"} +

{@render renderChilds(dom)}

- {:else if dom.tagName === 'H3'} -

+ {:else if dom.tagName === "H3"} +

{@render renderChilds(dom)}

- {:else if dom.tagName === 'H4'} -

+ {:else if dom.tagName === "H4"} +

{@render renderChilds(dom)}

- {:else if dom.tagName === 'H5'} -
+ {:else if dom.tagName === "H5"} +
{@render renderChilds(dom)}
- {:else if dom.tagName === 'H6'} -
+ {:else if dom.tagName === "H6"} +
{@render renderChilds(dom)}
- {:else if dom.tagName === 'UL'} -
    + {:else if dom.tagName === "UL"} +
      {@render renderChilds(dom)}
    - {:else if dom.tagName === 'OL'} -
      + {:else if dom.tagName === "OL"} +
        {@render renderChilds(dom)}
      - {:else if dom.tagName === 'LI'} -
    1. + {:else if dom.tagName === "LI"} +
    2. {@render renderChilds(dom)}
    3. - {:else if dom.tagName === 'TABLE'} - + {:else if dom.tagName === "TABLE"} +
      {@render renderChilds(dom)}
      - {:else if dom.tagName === 'TR'} - + {:else if dom.tagName === "TR"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'TD'} - + {:else if dom.tagName === "TD"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'TH'} - + {:else if dom.tagName === "TH"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'HR'} -
      - {:else if dom.tagName === 'BR'} -
      - {:else if dom.tagName === 'CODE'} - + {:else if dom.tagName === "HR"} +
      + {:else if dom.tagName === "BR"} +
      + {:else if dom.tagName === "CODE"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'PRE'} -
      +    {:else if dom.tagName === "PRE"}
      +        
                   {@render renderChilds(dom)}
               
      - {:else if dom.tagName === 'BLOCKQUOTE'} -
      + {:else if dom.tagName === "BLOCKQUOTE"} +
      {@render renderChilds(dom)}
      - {:else if dom.tagName === 'EM'} - + {:else if dom.tagName === "EM"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'STRONG'} - + {:else if dom.tagName === "STRONG"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'U'} - + {:else if dom.tagName === "U"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'DEL'} - + {:else if dom.tagName === "DEL"} + {@render renderChilds(dom)} - {:else if dom.tagName === 'BUTTON'} - - {:else if dom.tagName === 'STYLE'} + {:else if dom.tagName === "STYLE"} - {:else if dom.tagName === 'RISUTEXTBOX'} + {:else if dom.tagName === "RISUTEXTBOX"} {@render textBox()} - {:else if dom.tagName === 'RISUICON'} + {:else if dom.tagName === "RISUICON"} {@render icon()} - {:else if dom.tagName === 'RISUBUTTONS'} + {:else if dom.tagName === "RISUBUTTONS"} {@render icons()} - {:else if dom.tagName === 'RISUGENINFO'} + {:else if dom.tagName === "RISUGENINFO"} {@render genInfo()} - {:else if dom.tagName === 'STYLE'} - + {:else if dom.tagName === "STYLE"} + {dom.innerHTML} {:else} -
      +
      {@render renderChilds(dom)}
      {/if} - - {/snippet} -{#snippet renderChilds(dom:HTMLElement)} +{#snippet renderChilds(dom: HTMLElement)} {#each dom.childNodes as node} {#if node.nodeType === Node.TEXT_NODE} {node.textContent} {:else if node.nodeType === Node.ELEMENT_NODE} - {@render renderGuiHtmlPart((node as HTMLElement))} + {@render renderGuiHtmlPart(node as HTMLElement)} {/if} {/each} {/snippet} -
      -
      - {#if DBState.db.theme === 'mobilechat' && !blankMessage} -
      - {#if role !== 'user'} - {@render icon({rounded: true})} +
      +
      + {#if DBState.db.theme === "mobilechat" && !blankMessage} +
      + {#if role !== "user"} + {@render icon({ rounded: true })} {/if}

      {@render textBox()}

      {#if DBState.db.characters?.[selIdState.selId]?.chats?.[DBState.db.characters?.[selIdState.selId]?.chatPage]?.message?.[idx]?.time} {new Intl.DateTimeFormat(undefined, { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - month: '2-digit', - day: '2-digit', - hour12: false - }).format(DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message[idx].time)} + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + month: "2-digit", + day: "2-digit", + hour12: false, + }).format( + DBState.db.characters[selIdState.selId].chats[ + DBState.db.characters[selIdState.selId] + .chatPage + ].message[idx].time, + )} {/if}
      - {#if role === 'user'} - {@render icon({rounded: true})} + {#if role === "user"} + {@render icon({ rounded: true })} {/if}
      - {:else if DBState.db.theme === 'cardboard' && !blankMessage} + {:else if DBState.db.theme === "cardboard" && !blankMessage}
      -
      +
      - {@render icon({rounded: false, styleFix:'height:100%;width:100%;'})} + {@render icon({ + rounded: false, + styleFix: "height:100%;width:100%;", + })}
      -

      {name}

      - +

      + {name} +

      {#if editMode} - + {:else} -
      +
      {@render textBox()}
      {/if}
      -
      - {@render icons({applyTextColors: false})} +
      + {@render icons({ applyTextColors: false })}
      - {:else if DBState.db.theme === 'customHTML' && !blankMessage} + {:else if DBState.db.theme === "customHTML" && !blankMessage} {@render renderGuiHtmlPart(RenderGUIHtml(DBState.db.guiHTML))} {:else} - {@render icon({rounded: DBState.db.roundIcons})} - + {@render icon({ rounded: DBState.db.roundIcons })} +
      {#if DBState.db.characters[selIdState.selId]?.chaId === "§playground" && !blankMessage} - - {DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message[idx].role === 'char' ? 'Assistant' : 'User'} - + + {DBState.db.characters[selIdState.selId].chats[ + DBState.db.characters[selIdState.selId] + .chatPage + ].message[idx].role === "char" + ? "Assistant" + : "User"} + {:else if !blankMessage && !$HideIconStore} - {name} + {name} {/if} {@render icons()}
      diff --git a/src/ts/observer.svelte.ts b/src/ts/observer.svelte.ts index 03748a02a9..844cb904ef 100644 --- a/src/ts/observer.svelte.ts +++ b/src/ts/observer.svelte.ts @@ -1,21 +1,21 @@ import { runTrigger } from "./process/triggers"; import { sleep } from "./util"; import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte"; -import { runLuaButtonTrigger } from "./process/scriptings"; +import { runLuaButtonTrigger } from "./process/script/trigger"; import { globalFetch } from "./globalApi.svelte"; -let bgmElement:HTMLAudioElement|null = null; +let bgmElement: HTMLAudioElement | null = null; -function nodeObserve(node:HTMLElement){ +function nodeObserve(node: HTMLElement) { const hlLang = node.getAttribute('x-hl-lang'); const ctrlName = node.getAttribute('risu-ctrl'); - if(hlLang){ - node.addEventListener('contextmenu', (e)=>{ + if (hlLang) { + node.addEventListener('contextmenu', (e) => { e.preventDefault() const prevContextMenu = document.getElementById('code-contextmenu') - if(prevContextMenu){ + if (prevContextMenu) { prevContextMenu.remove() } @@ -26,7 +26,7 @@ function nodeObserve(node:HTMLElement){ const copyOption = document.createElement('div') copyOption.textContent = 'Copy' copyOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer') - copyOption.addEventListener('click', ()=>{ + copyOption.addEventListener('click', () => { navigator.clipboard.writeText(node.textContent) menu.remove() }) @@ -34,9 +34,9 @@ function nodeObserve(node:HTMLElement){ const downloadOption = document.createElement('div'); downloadOption.textContent = 'Download'; downloadOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer') - downloadOption.addEventListener('click', ()=>{ + downloadOption.addEventListener('click', () => { const a = document.createElement('a') - a.href = URL.createObjectURL(new Blob([node.textContent], {type: 'text/plain'})) + a.href = URL.createObjectURL(new Blob([node.textContent], { type: 'text/plain' })) a.download = 'code.' + hlLang a.click() menu.remove() @@ -49,23 +49,23 @@ function nodeObserve(node:HTMLElement){ menu.style.top = e.clientY + 'px' document.body.appendChild(menu) - - document.addEventListener('click', ()=>{ + + document.addEventListener('click', () => { menu?.remove() - }, {once: true}) + }, { once: true }) }) } - if(ctrlName){ + if (ctrlName) { const split = ctrlName.split('___'); - switch(split[0]){ - case 'bgm':{ + switch (split[0]) { + case 'bgm': { const volume = split[1] === 'auto' ? 0.5 : parseFloat(split[1]); - if(!bgmElement){ + if (!bgmElement) { bgmElement = new Audio(split[2]); bgmElement.volume = volume - bgmElement.addEventListener('ended', ()=>{ + bgmElement.addEventListener('ended', () => { bgmElement.remove(); bgmElement = null; }) @@ -77,19 +77,19 @@ function nodeObserve(node:HTMLElement){ } } -export async function startObserveDom(){ +export async function startObserveDom() { //For codeblock we are using MutationObserver since it doesn't appear well const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { - if(node instanceof HTMLElement){ + if (node instanceof HTMLElement) { nodeObserve(node); } }) }) }) - while(true){ + while (true) { document.querySelectorAll('[x-hl-lang], [risu-ctrl]').forEach(nodeObserve); await sleep(100); } @@ -99,14 +99,14 @@ export async function startObserveDom(){ let claudeObserverRunning = false; let lastClaudeObserverLoad = 0; let lastClaudeRequestTimes = 0; -let lastClaudeObserverPayload:any = null; -let lastClaudeObserverHeaders:any = null; -let lastClaudeObserverURL:any = null; - -export async function registerClaudeObserver(arg:{ - url:string, - body:any, - headers:any, +let lastClaudeObserverPayload: any = null; +let lastClaudeObserverHeaders: any = null; +let lastClaudeObserverURL: any = null; + +export async function registerClaudeObserver(arg: { + url: string, + body: any, + headers: any, }) { lastClaudeRequestTimes = 0; lastClaudeObserverLoad = Date.now(); @@ -117,38 +117,38 @@ export async function registerClaudeObserver(arg:{ claudeObserver() } -async function claudeObserver(){ - if(claudeObserverRunning){ +async function claudeObserver() { + if (claudeObserverRunning) { return } claudeObserverRunning = true; - const fetchIt = async (tries = 0)=>{ + const fetchIt = async (tries = 0) => { const res = await globalFetch(lastClaudeObserverURL, { body: lastClaudeObserverPayload, headers: lastClaudeObserverHeaders, method: "POST" }) - if(res.status >= 400){ - if(tries < 3){ + if (res.status >= 400) { + if (tries < 3) { fetchIt(tries + 1) } } } - const func = async ()=>{ + const func = async () => { //request every 4 minutes and 30 seconds - if(lastClaudeObserverLoad > Date.now() - 1000 * 60 * 4.5){ + if (lastClaudeObserverLoad > Date.now() - 1000 * 60 * 4.5) { return } - - if(lastClaudeRequestTimes > 4){ + + if (lastClaudeRequestTimes > 4) { return } fetchIt() lastClaudeObserverLoad = Date.now(); lastClaudeRequestTimes += 1; } - + setInterval(func, 20000) } \ No newline at end of file diff --git a/src/ts/process/index.svelte.ts b/src/ts/process/index.svelte.ts index 780173307a..1656f1a023 100644 --- a/src/ts/process/index.svelte.ts +++ b/src/ts/process/index.svelte.ts @@ -26,71 +26,71 @@ import { addRerolls } from "./prereroll"; import { runImageEmbedding } from "./transformers"; import { hanuraiMemory } from "./memory/hanuraiMemory"; import { hypaMemoryV2 } from "./memory/hypav2"; -import { runLuaEditTrigger } from "./scriptings"; +import { runLuaEditTrigger } from "src/ts/process/script/trigger"; import { getGlobalChatVar, parseChatML } from "../parser.svelte"; import { getModelInfo, LLMFlags } from "../model/modellist"; import { hypaMemoryV3 } from "./memory/hypav3"; import { getModuleAssets, getModuleToggles } from "./modules"; import { getFileSrc, readImage } from "../globalApi.svelte"; -export interface OpenAIChat{ - role: 'system'|'user'|'assistant'|'function' +export interface OpenAIChat { + role: 'system' | 'user' | 'assistant' | 'function' content: string - memo?:string - name?:string - removable?:boolean - attr?:string[] + memo?: string + name?: string + removable?: boolean + attr?: string[] multimodals?: MultiModal[] thoughts?: string[] cachePoint?: boolean } -export interface MultiModal{ - type:'image'|'video'|'audio' - base64:string, - height?:number, - width?:number +export interface MultiModal { + type: 'image' | 'video' | 'audio' + base64: string, + height?: number, + width?: number } -export interface OpenAIChatFull extends OpenAIChat{ +export interface OpenAIChatFull extends OpenAIChat { function_call?: { name: string - arguments:string + arguments: string } - tool_calls?:{ + tool_calls?: { function: { name: string - arguments:string + arguments: string } - id:string - type:'function' + id: string + type: 'function' }[] } -export interface requestTokenPart{ - name:string - tokens:number +export interface requestTokenPart { + name: string + tokens: number } export const doingChat = writable(false) export const chatProcessStage = writable(0) export const abortChat = writable(false) -export let requestTokenParts:{[key:string]:requestTokenPart[]} = {} -export let previewFormated:OpenAIChat[] = [] -export let previewBody:string = '' - -export async function sendChat(chatProcessIndex = -1,arg:{ - chatAdditonalTokens?:number, - signal?:AbortSignal, - continue?:boolean, - usedContinueTokens?:number, - preview?:boolean - previewPrompt?:boolean -} = {}):Promise { +export let requestTokenParts: { [key: string]: requestTokenPart[] } = {} +export let previewFormated: OpenAIChat[] = [] +export let previewBody: string = '' + +export async function sendChat(chatProcessIndex = -1, arg: { + chatAdditonalTokens?: number, + signal?: AbortSignal, + continue?: boolean, + usedContinueTokens?: number, + preview?: boolean + previewPrompt?: boolean +} = {}): Promise { chatProcessStage.set(0) const abortSignal = arg.signal ?? (new AbortController()).signal - + const stageTimings = { stage1Start: 0, stage2Start: 0, @@ -103,13 +103,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } let isAborted = false - let findCharCache:{[key:string]:character} = {} - function findCharacterbyIdwithCache(id:string){ + let findCharCache: { [key: string]: character } = {} + function findCharacterbyIdwithCache(id: string) { const d = findCharCache[id] - if(!!d){ + if (!!d) { return d } - else{ + else { const r = findCharacterbyId(id) findCharCache[id] = r return r @@ -117,28 +117,28 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } - function runCurrentChatFunction(chat:Chat){ + function runCurrentChatFunction(chat: Chat) { chat.message = chat.message.map((v) => { - v.data = risuChatParser(v.data, {chara: currentChar, runVar: true}) + v.data = risuChatParser(v.data, { chara: currentChar, runVar: true }) return v }) return chat } - function reformatContent(data:string){ - if(chatProcessIndex === -1){ + function reformatContent(data: string) { + if (chatProcessIndex === -1) { return data.trim() } return data.trim() } - function throwError(error:string){ + function throwError(error: string) { - if(DBState.db.inlayErrorResponse){ - if(DBState.db.characters[selectedChar].chats[selectedChat].message[DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1].role === 'char'){ + if (DBState.db.inlayErrorResponse) { + if (DBState.db.characters[selectedChar].chats[selectedChat].message[DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1].role === 'char') { DBState.db.characters[selectedChar].chats[selectedChat].message[DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1].data += `\n\`\`\`risuerror\n${error}\n\`\`\`` } - else{ + else { DBState.db.characters[selectedChar].chats[selectedChat].message.push({ role: 'char', @@ -158,14 +158,14 @@ export async function sendChat(chatProcessIndex = -1,arg:{ let isDoing = get(doingChat) - if(isDoing){ - if(chatProcessIndex === -1){ + if (isDoing) { + if (chatProcessIndex === -1) { return false } } doingChat.set(true) - if(chatProcessIndex === -1 && DBState.db.presetChain){ + if (chatProcessIndex === -1 && DBState.db.presetChain) { const names = DBState.db.presetChain.split(',').map((v) => v.trim()) const randomSelect = Math.floor(Math.random() * names.length) const ele = names[randomSelect] @@ -174,18 +174,18 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return v.name === ele }) - if(findId === -1){ + if (findId === -1) { alertToast(`Cannot find preset: ${ele}`) } - else{ + else { changeToPreset(findId, true) } } - if(connectionOpen){ + if (connectionOpen) { chatProcessStage.set(4) const peerSafe = await peerSafeCheck() - if(!peerSafe){ + if (!peerSafe) { peerRevertChat() doingChat.set(false) throwError(language.otherUserRequesting) @@ -204,21 +204,21 @@ export async function sendChat(chatProcessIndex = -1,arg:{ v.chatId = v.chatId ?? v4() return v }) - -// ───────────────────────────────────────────────────────── -// Snapshot preset name & toggles before sending a message. -// Ensures correct metadata is recorded, even if presets -// change immediately after clicking "send". -// -// Used later in promptInfo assembly (e.g. promptInfo.promptText) -// ───────────────────────────────────────────────────────── + + // ───────────────────────────────────────────────────────── + // Snapshot preset name & toggles before sending a message. + // Ensures correct metadata is recorded, even if presets + // change immediately after clicking "send". + // + // Used later in promptInfo assembly (e.g. promptInfo.promptText) + // ───────────────────────────────────────────────────────── let promptInfo: MessagePresetInfo = {} let initialPresetNameForPromptInfo = null let initialPromptTogglesForPromptInfo: { key: string, value: string, }[] = [] - if(DBState.db.promptInfoInsideChat){ + if (DBState.db.promptInfoInsideChat) { initialPresetNameForPromptInfo = DBState.db.botPresets[DBState.db.botPresetsId]?.name ?? '' initialPromptTogglesForPromptInfo = parseToggleSyntax(DBState.db.customPromptTemplateToggle + getModuleToggles()) .flatMap(toggle => { @@ -237,24 +237,24 @@ export async function sendChat(chatProcessIndex = -1,arg:{ promptToggles: initialPromptTogglesForPromptInfo, } } -// ───────────────────────────────────────────────────────────── + // ───────────────────────────────────────────────────────────── - let currentChar:character + let currentChar: character let caculatedChatTokens = 0 - if(DBState.db.aiModel.startsWith('gpt')){ + if (DBState.db.aiModel.startsWith('gpt')) { caculatedChatTokens += 5 } - else{ + else { caculatedChatTokens += 3 } - if(nowChatroom.type === 'group'){ - if(chatProcessIndex === -1){ - const charNames =nowChatroom.characters.map((v) => findCharacterbyIdwithCache(v).name) + if (nowChatroom.type === 'group') { + if (chatProcessIndex === -1) { + const charNames = nowChatroom.characters.map((v) => findCharacterbyIdwithCache(v).name) const messages = nowChatroom.chats[nowChatroom.chatPage].message - const lastMessage = messages[messages.length-1] - let order = nowChatroom.characters.map((v,i) => { + const lastMessage = messages[messages.length - 1] + let order = nowChatroom.characters.map((v, i) => { return { id: v, talkness: nowChatroom.characterActive[i] ? nowChatroom.characterTalks[i] : -1, @@ -263,34 +263,34 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }).filter((v) => { return v.talkness > 0 }) - if(!nowChatroom.orderByOrder){ + if (!nowChatroom.orderByOrder) { order = groupOrder(order, lastMessage?.data).filter((v) => { - if(v.id === lastMessage?.saying){ + if (v.id === lastMessage?.saying) { return false } return true }) } - for(let i=0;i 4000){ + if (DBState.db.aiModel === 'gpt35') { + if (maxContextTokens > 4000) { maxContextTokens = 4000 } } - if(DBState.db.aiModel === 'gpt35_16k' || DBState.db.aiModel === 'gpt35_16k_0613'){ - if(maxContextTokens > 16000){ + if (DBState.db.aiModel === 'gpt35_16k' || DBState.db.aiModel === 'gpt35_16k_0613') { + if (maxContextTokens > 16000) { maxContextTokens = 16000 } } - if(DBState.db.aiModel === 'gpt4'){ - if(maxContextTokens > 8000){ + if (DBState.db.aiModel === 'gpt4') { + if (maxContextTokens > 8000) { maxContextTokens = 8000 } } - if(DBState.db.aiModel === 'deepai'){ - if(maxContextTokens > 3000){ + if (DBState.db.aiModel === 'deepai') { + if (maxContextTokens > 3000) { maxContextTokens = 3000 } } @@ -325,59 +325,59 @@ export async function sendChat(chatProcessIndex = -1,arg:{ chatProcessStage.set(1) stageTimings.stage1Start = Date.now() let unformated = { - 'main':([] as OpenAIChat[]), - 'jailbreak':([] as OpenAIChat[]), - 'chats':([] as OpenAIChat[]), - 'lorebook':([] as OpenAIChat[]), - 'globalNote':([] as OpenAIChat[]), - 'authorNote':([] as OpenAIChat[]), - 'lastChat':([] as OpenAIChat[]), - 'description':([] as OpenAIChat[]), - 'postEverything':([] as OpenAIChat[]), - 'personaPrompt':([] as OpenAIChat[]) + 'main': ([] as OpenAIChat[]), + 'jailbreak': ([] as OpenAIChat[]), + 'chats': ([] as OpenAIChat[]), + 'lorebook': ([] as OpenAIChat[]), + 'globalNote': ([] as OpenAIChat[]), + 'authorNote': ([] as OpenAIChat[]), + 'lastChat': ([] as OpenAIChat[]), + 'description': ([] as OpenAIChat[]), + 'postEverything': ([] as OpenAIChat[]), + 'personaPrompt': ([] as OpenAIChat[]) } let promptTemplate = safeStructuredClone(DBState.db.promptTemplate) const usingPromptTemplate = !!promptTemplate - if(promptTemplate){ + if (promptTemplate) { let hasPostEverything = false - for(const card of promptTemplate){ - if(card.type === 'postEverything'){ + for (const card of promptTemplate) { + if (card.type === 'postEverything') { hasPostEverything = true break } } - if(!hasPostEverything){ + if (!hasPostEverything) { promptTemplate.push({ type: 'postEverything' }) } } - if(currentChar.utilityBot && (!(usingPromptTemplate && DBState.db.promptSettings.utilOverride))){ + if (currentChar.utilityBot && (!(usingPromptTemplate && DBState.db.promptSettings.utilOverride))) { promptTemplate = [ { - "type": "plain", - "text": "", - "role": "system", - "type2": "main" + "type": "plain", + "text": "", + "role": "system", + "type2": "main" }, { - "type": "description", + "type": "description", }, { - "type": "lorebook", + "type": "lorebook", }, { - "type": "chat", - "rangeStart": 0, - "rangeEnd": "end" + "type": "chat", + "rangeStart": 0, + "rangeEnd": "end" }, { - "type": "plain", - "text": "", - "role": "system", - "type2": "globalNote" + "type": "plain", + "text": "", + "role": "system", + "type2": "globalNote" }, { 'type': "postEverything" @@ -385,52 +385,52 @@ export async function sendChat(chatProcessIndex = -1,arg:{ ] } - if((!currentChar.utilityBot) && (!promptTemplate)){ + if ((!currentChar.utilityBot) && (!promptTemplate)) { const mainp = currentChar.systemPrompt?.replaceAll('{{original}}', DBState.db.mainPrompt) || DBState.db.mainPrompt - function formatPrompt(data:string){ - if(!data.startsWith('@@')){ + function formatPrompt(data: string) { + if (!data.startsWith('@@')) { data = "@@system\n" + data } const parts = data.split(/@@@?(user|assistant|system)\n/); - + // Initialize empty array for the chat objects const chatObjects: OpenAIChat[] = []; - + // Loop through the parts array two elements at a time for (let i = 1; i < parts.length; i += 2) { - const role = parts[i] as 'user' | 'assistant' | 'system'; - const content = parts[i + 1]?.trim() || ''; - chatObjects.push({ role, content }); + const role = parts[i] as 'user' | 'assistant' | 'system'; + const content = parts[i + 1]?.trim() || ''; + chatObjects.push({ role, content }); } return chatObjects; } - unformated.main.push(...formatPrompt(risuChatParser(mainp + ((DBState.db.additionalPrompt === '' || (!DBState.db.promptPreprocess)) ? '' : `\n${DBState.db.additionalPrompt}`), {chara: currentChar}))) - - if(DBState.db.jailbreakToggle){ - unformated.jailbreak.push(...formatPrompt(risuChatParser(DBState.db.jailbreak, {chara: currentChar}))) + unformated.main.push(...formatPrompt(risuChatParser(mainp + ((DBState.db.additionalPrompt === '' || (!DBState.db.promptPreprocess)) ? '' : `\n${DBState.db.additionalPrompt}`), { chara: currentChar }))) + + if (DBState.db.jailbreakToggle) { + unformated.jailbreak.push(...formatPrompt(risuChatParser(DBState.db.jailbreak, { chara: currentChar }))) } - - unformated.globalNote.push(...formatPrompt(risuChatParser(currentChar.replaceGlobalNote?.replaceAll('{{original}}', DBState.db.globalNote) || DBState.db.globalNote, {chara:currentChar}))) + + unformated.globalNote.push(...formatPrompt(risuChatParser(currentChar.replaceGlobalNote?.replaceAll('{{original}}', DBState.db.globalNote) || DBState.db.globalNote, { chara: currentChar }))) } - if(currentChat.note){ + if (currentChat.note) { unformated.authorNote.push({ role: 'system', - content: risuChatParser(currentChat.note, {chara: currentChar}) + content: risuChatParser(currentChat.note, { chara: currentChar }) }) } - else if(getAuthorNoteDefaultText() !== ''){ + else if (getAuthorNoteDefaultText() !== '') { unformated.authorNote.push({ role: 'system', - content: risuChatParser(getAuthorNoteDefaultText(), {chara: currentChar}) + content: risuChatParser(getAuthorNoteDefaultText(), { chara: currentChar }) }) } - if(DBState.db.chainOfThought && (!(usingPromptTemplate && DBState.db.promptSettings.customChainOfThought))){ + if (DBState.db.chainOfThought && (!(usingPromptTemplate && DBState.db.promptSettings.customChainOfThought))) { unformated.postEverything.push({ role: 'system', content: ` - before respond everything, Think step by step as a ai assistant how would you respond inside xml tag. this must be less than 5 paragraphs.` @@ -438,20 +438,20 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } { - let description = risuChatParser((DBState.db.promptPreprocess ? DBState.db.descriptionPrefix: '') + currentChar.desc, {chara: currentChar}) + let description = risuChatParser((DBState.db.promptPreprocess ? DBState.db.descriptionPrefix : '') + currentChar.desc, { chara: currentChar }) const additionalInfo = await additionalInformations(currentChar, currentChat) - if(additionalInfo){ - description += '\n\n' + risuChatParser(additionalInfo, {chara:currentChar}) + if (additionalInfo) { + description += '\n\n' + risuChatParser(additionalInfo, { chara: currentChar }) } - if(currentChar.personality){ - description += risuChatParser("\n\nDescription of {{char}}: " + currentChar.personality, {chara: currentChar}) + if (currentChar.personality) { + description += risuChatParser("\n\nDescription of {{char}}: " + currentChar.personality, { chara: currentChar }) } - if(currentChar.scenario){ - description += risuChatParser("\n\nCircumstances and context of the dialogue: " + currentChar.scenario, {chara: currentChar}) + if (currentChar.scenario) { + description += risuChatParser("\n\nCircumstances and context of the dialogue: " + currentChar.scenario, { chara: currentChar }) } unformated.description.push({ @@ -459,7 +459,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ content: description }) - if(nowChatroom.type === 'group'){ + if (nowChatroom.type === 'group') { const systemMsg = `[Write the next reply only as ${currentChar.name}]` unformated.postEverything.push({ role: 'system', @@ -474,10 +474,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) console.log(normalActives) - for(const lorebook of normalActives){ + for (const lorebook of normalActives) { unformated.lorebook.push({ role: lorebook.role, - content: risuChatParser(lorebook.prompt, {chara: currentChar}) + content: risuChatParser(lorebook.prompt, { chara: currentChar }) }) } @@ -485,34 +485,34 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return v.pos === 'after_desc' || v.pos === 'before_desc' || v.pos === 'personality' || v.pos === 'scenario' }) - for(const lorebook of descActives){ + for (const lorebook of descActives) { const c = { role: lorebook.role, - content: risuChatParser(lorebook.prompt, {chara: currentChar}) + content: risuChatParser(lorebook.prompt, { chara: currentChar }) } - if(lorebook.pos === 'before_desc'){ + if (lorebook.pos === 'before_desc') { unformated.description.unshift(c) } - else{ + else { unformated.description.push(c) } } - if(DBState.db.personaPrompt){ + if (DBState.db.personaPrompt) { unformated.personaPrompt.push({ role: 'system', - content: risuChatParser(getPersonaPrompt(), {chara: currentChar}) + content: risuChatParser(getPersonaPrompt(), { chara: currentChar }) }) } - - if(currentChar.inlayViewScreen){ - if(currentChar.viewScreen === 'emotion'){ + + if (currentChar.inlayViewScreen) { + if (currentChar.viewScreen === 'emotion') { unformated.postEverything.push({ role: 'system', content: currentChar.newGenData.emotionInstructions.replaceAll('{{slot}}', currentChar.emotionImages.map((v) => v[0]).join(', ')) }) } - if(currentChar.viewScreen === 'imggen'){ + if (currentChar.viewScreen === 'imggen') { unformated.postEverything.push({ role: 'system', content: currentChar.newGenData.instructions @@ -523,10 +523,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{ const postEverythingLorebooks = lorepmt.actives.filter(v => { return v.pos === 'depth' && v.depth === 0 && v.role !== 'assistant' }) - for(const lorebook of postEverythingLorebooks){ + for (const lorebook of postEverythingLorebooks) { unformated.postEverything.push({ role: lorebook.role, - content: risuChatParser(lorebook.prompt, {chara: currentChar}) + content: risuChatParser(lorebook.prompt, { chara: currentChar }) }) } @@ -540,42 +540,42 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) const injectionLorePosSet = new Set() - for(const lorebook of injectionLorebooks){ + for (const lorebook of injectionLorebooks) { injectionLorePosSet.add(lorebook.inject.location) } - - for(const lorebook of postEverythingAssistantLorebooks){ + + for (const lorebook of postEverythingAssistantLorebooks) { unformated.postEverything.push({ role: lorebook.role, - content: risuChatParser(lorebook.prompt, {chara: currentChar}) + content: risuChatParser(lorebook.prompt, { chara: currentChar }) }) } //await tokenize currernt let currentTokens = DBState.db.maxResponse let supaMemoryCardUsed = false - + //for unexpected error currentTokens += 50 - + const positionRegex = /{{position::(.+?)}}/g - const positionParser = (text:string, loc:string) => { + const positionParser = (text: string, loc: string) => { console.log(injectionLorePosSet) - if(injectionLorePosSet.has(loc)){ + if (injectionLorePosSet.has(loc)) { const matchings = injectionLorebooks.filter(v => { return v.inject.location === loc }) - for(const lore of matchings){ - switch(lore.inject.operation){ - case 'append':{ + for (const lore of matchings) { + switch (lore.inject.operation) { + case 'append': { text += ' ' + lore.prompt break } - case 'prepend':{ + case 'prepend': { text = lore.prompt + ' ' + text break } - case 'replace':{ + case 'replace': { text = text.replace(lore.inject.param, lore.prompt) break } @@ -594,58 +594,58 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } let hasCachePoint = false - if(promptTemplate){ + if (promptTemplate) { const template = promptTemplate - async function tokenizeChatArray(chats:OpenAIChat[]){ - for(const chat of chats){ + async function tokenizeChatArray(chats: OpenAIChat[]) { + for (const chat of chats) { const tokens = await tokenizer.tokenizeChat(chat) currentTokens += tokens } } - for(const card of template){ - switch(card.type){ - case 'persona':{ + for (const card of template) { + switch (card.type) { + case 'persona': { let pmt = safeStructuredClone(unformated.personaPrompt) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content) } } await tokenizeChatArray(pmt) break } - case 'description':{ + case 'description': { let pmt = safeStructuredClone(unformated.description) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content) } } await tokenizeChatArray(pmt) break } - case 'authornote':{ + case 'authornote': { let pmt = safeStructuredClone(unformated.authorNote) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content || card.defaultText || '') } } await tokenizeChatArray(pmt) break } - case 'lorebook':{ + case 'lorebook': { await tokenizeChatArray(unformated.lorebook) break } - case 'postEverything':{ + case 'postEverything': { await tokenizeChatArray(unformated.postEverything) - if(usingPromptTemplate && DBState.db.promptSettings.postEndInnerFormat){ + if (usingPromptTemplate && DBState.db.promptSettings.postEndInnerFormat) { await tokenizeChatArray([{ role: 'system', content: DBState.db.promptSettings.postEndInnerFormat @@ -655,11 +655,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } case 'plain': case 'jailbreak': - case 'cot':{ - if((!DBState.db.jailbreakToggle) && (card.type === 'jailbreak')){ + case 'cot': { + if ((!DBState.db.jailbreakToggle) && (card.type === 'jailbreak')) { continue } - if((!DBState.db.chainOfThought) && (card.type === 'cot')){ + if ((!DBState.db.chainOfThought) && (card.type === 'cot')) { continue } @@ -672,24 +672,24 @@ export async function sendChat(chatProcessIndex = -1,arg:{ const posType = card.type === 'plain' ? card.type2 : card.type let content = positionParser(card.text, posType) - if(card.type2 === 'globalNote'){ - if(currentChar.replaceGlobalNote){ + if (card.type2 === 'globalNote') { + if (currentChar.replaceGlobalNote) { content = positionParser(currentChar.replaceGlobalNote, posType).replaceAll('{{original}}', content) } - - if(currentChar.prebuiltAssetCommand && !card.text.includes('{{//@customimageinstruction}}')){ + + if (currentChar.prebuiltAssetCommand && !card.text.includes('{{//@customimageinstruction}}')) { content += prebuiltAssetCommand } - content = (risuChatParser(content, {chara: currentChar, role: card.role})) + content = (risuChatParser(content, { chara: currentChar, role: card.role })) } - else if(card.type2 === 'main'){ - content = (risuChatParser(content, {chara: currentChar, role: card.role})) + else if (card.type2 === 'main') { + content = (risuChatParser(content, { chara: currentChar, role: card.role })) } - else{ - content = risuChatParser(content, {chara: currentChar, role: card.role}) + else { + content = risuChatParser(content, { chara: currentChar, role: card.role }) } - const prompt:OpenAIChat ={ + const prompt: OpenAIChat = { role: convertRole[card.role], content: content } @@ -697,71 +697,71 @@ export async function sendChat(chatProcessIndex = -1,arg:{ await tokenizeChatArray([prompt]) break } - case 'chatML':{ + case 'chatML': { let prompts = parseChatML(card.text) await tokenizeChatArray(prompts) break } - case 'chat':{ + case 'chat': { let start = card.rangeStart let end = (card.rangeEnd === 'end') ? unformated.chats.length : card.rangeEnd - if(start === -1000){ + if (start === -1000) { start = 0 end = unformated.chats.length } - if(start < 0){ + if (start < 0) { start = unformated.chats.length + start - if(start < 0){ + if (start < 0) { start = 0 } } - if(end < 0){ + if (end < 0) { end = unformated.chats.length + end - if(end < 0){ + if (end < 0) { end = 0 } } - - if(start >= end){ + + if (start >= end) { break } let chats = unformated.chats.slice(start, end) - if(usingPromptTemplate && DBState.db.promptSettings.sendChatAsSystem && (!card.chatAsOriginalOnSystem)){ + if (usingPromptTemplate && DBState.db.promptSettings.sendChatAsSystem && (!card.chatAsOriginalOnSystem)) { chats = systemizeChat(chats) } await tokenizeChatArray(chats) break } - case 'memory':{ + case 'memory': { supaMemoryCardUsed = true break } - case 'cache':{ + case 'cache': { hasCachePoint = true break } } } } - else{ - for(const key in unformated){ + else { + for (const key in unformated) { const chats = unformated[key] as OpenAIChat[] - for(const chat of chats){ + for (const chat of chats) { currentTokens += await tokenizer.tokenizeChat(chat) } } } - + const examples = exampleMessage(currentChar, getUserName()) - for(const example of examples){ + for (const example of examples) { currentTokens += await tokenizer.tokenizeChat(example) } - let chats:OpenAIChat[] = examples + let chats: OpenAIChat[] = examples - if(!DBState.db.aiModel.startsWith('novelai')){ + if (!DBState.db.aiModel.startsWith('novelai')) { chats.push({ role: 'system', content: '[Start a new chat]', @@ -769,17 +769,17 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) } - if(nowChatroom.type !== 'group'){ + if (nowChatroom.type !== 'group') { const firstMsg = currentChat.fmIndex === -1 ? nowChatroom.firstMessage : nowChatroom.alternateGreetings[currentChat.fmIndex] - const chat:OpenAIChat = { + const chat: OpenAIChat = { role: 'assistant', content: await (processScript(nowChatroom, - risuChatParser(firstMsg, {chara: currentChar}), - 'editprocess')) + risuChatParser(firstMsg, { chara: currentChar }), + 'editprocess')) } - if(usingPromptTemplate && DBState.db.promptSettings.sendName){ + if (usingPromptTemplate && DBState.db.promptSettings.sendName) { chat.content = `${currentChar.name}: ${chat.content}` chat.attr = ['nameAdded'] } @@ -789,68 +789,68 @@ export async function sendChat(chatProcessIndex = -1,arg:{ let ms = currentChat.message - const triggerResult = await runTrigger(currentChar, 'start', {chat: currentChat}) - if(triggerResult){ + const triggerResult = await runTrigger(currentChar, 'start', { chat: currentChat }) + if (triggerResult) { currentChat = triggerResult.chat setCurrentChat(currentChat) ms = currentChat.message currentTokens += triggerResult.tokens - if(triggerResult.stopSending){ + if (triggerResult.stopSending) { doingChat.set(false) return false } } let index = 0 - for(const msg of ms){ - let formatedChat = (await processScriptFull(nowChatroom,risuChatParser(msg.data, {chara: currentChar, role: msg.role}), 'editprocess', index, { + for (const msg of ms) { + let formatedChat = (await processScriptFull(nowChatroom, risuChatParser(msg.data, { chara: currentChar, role: msg.role }), 'editprocess', index, { chatRole: msg.role, })).data let name = '' - if(msg.role === 'char'){ - if(msg.saying){ + if (msg.role === 'char') { + if (msg.saying) { name = `${findCharacterbyIdwithCache(msg.saying).name}` } - else{ + else { name = `${currentChar.name}` } } - else if(msg.role === 'user'){ + else if (msg.role === 'user') { name = `${getUserName()}` } - if(!msg.chatId){ + if (!msg.chatId) { msg.chatId = v4() } - let inlays:string[] = [] - if(msg.role === 'char'){ + let inlays: string[] = [] + if (msg.role === 'char') { formatedChat = formatedChat.replace(/{{(inlay|inlayed|inlayeddata)::(.+?)}}/g, ( match: string, p1: string, p2: string ) => { - if(p2 && p1 === 'inlayeddata'){ + if (p2 && p1 === 'inlayeddata') { inlays.push(p2) } return '' }) } - else{ + else { const inlayMatch = formatedChat.match(/{{(inlay|inlayed|inlayeddata)::(.+?)}}/g) - if(inlayMatch){ - for(const inlay of inlayMatch){ + if (inlayMatch) { + for (const inlay of inlayMatch) { inlays.push(inlay) } } } - let multimodal:MultiModal[] = [] + let multimodal: MultiModal[] = [] const modelinfo = getModelInfo(DBState.db.aiModel) - if(inlays.length > 0){ - for(const inlay of inlays){ + if (inlays.length > 0) { + for (const inlay of inlays) { const inlayName = inlay.replace('{{inlayed::', '').replace('{{inlay::', '').replace('}}', '') const inlayData = await getInlayAsset(inlayName) - if(inlayData?.type === 'image'){ - if(modelinfo.flags.includes(LLMFlags.hasImageInput)){ + if (inlayData?.type === 'image') { + if (modelinfo.flags.includes(LLMFlags.hasImageInput)) { multimodal.push({ type: 'image', base64: inlayData.data, @@ -858,13 +858,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ height: inlayData.height }) } - else{ - const captionResult = await runImageEmbedding(inlayData.data) + else { + const captionResult = await runImageEmbedding(inlayData.data) formatedChat += `[${captionResult[0].generated_text}]` } } - if(inlayData?.type === 'video' || inlayData?.type === 'audio'){ - if(multimodal.length === 0){ + if (inlayData?.type === 'video' || inlayData?.type === 'audio') { + if (multimodal.length === 0) { multimodal.push({ type: inlayData.type, base64: inlayData.data @@ -875,17 +875,17 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } - let attr:string[] = [] - let role:'user'|'assistant'|'system' = msg.role === 'user' ? 'user' : 'assistant' + let attr: string[] = [] + let role: 'user' | 'assistant' | 'system' = msg.role === 'user' ? 'user' : 'assistant' - if( + if ( (nowChatroom.type === 'group' && findCharacterbyIdwithCache(msg.saying).chaId !== currentChar.chaId) || (nowChatroom.type === 'group' && DBState.db.groupOtherBotRole === 'assistant') || (usingPromptTemplate && DBState.db.promptSettings.sendName) - ){ + ) { const form = DBState.db.groupTemplate || `<{{char}}\'s Message>\n{{slot}}\n` - formatedChat = risuChatParser(form, {chara: findCharacterbyIdwithCache(msg.saying).name}).replace('{{slot}}', formatedChat) - switch(DBState.db.groupOtherBotRole){ + formatedChat = risuChatParser(form, { chara: findCharacterbyIdwithCache(msg.saying).name }).replace('{{slot}}', formatedChat) + switch (DBState.db.groupOtherBotRole) { case 'user': case 'assistant': case 'system': @@ -896,23 +896,23 @@ export async function sendChat(chatProcessIndex = -1,arg:{ break } } - let thoughts:string[] = [] + let thoughts: string[] = [] const maxThoughtDepth = DBState.db.promptSettings?.maxThoughtTagDepth ?? -1 formatedChat = formatedChat.replace(/(.+)<\/Thoughts>/gms, (match, p1) => { - if(maxThoughtDepth === -1 || (maxThoughtDepth - ms.length) <= index){ + if (maxThoughtDepth === -1 || (maxThoughtDepth - ms.length) <= index) { thoughts.push(p1) } return '' }) - const assetPromises:Promise[] = [] + const assetPromises: Promise[] = [] formatedChat = formatedChat.replace(/\{\{asset_?prompt::(.+?)\}\}/gmsiu, (match, p1) => { const moduleAssets = getModuleAssets() const assets = (currentChar.additionalAssets ?? []).concat(moduleAssets) const asset = assets.find(v => { return v[0] === p1 }) - if(asset){ + if (asset) { assetPromises.push((async () => { const assetDataBuf = await readImage(asset[1]) multimodal.push({ @@ -921,7 +921,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) })()) } - else if(p1 === 'icon'){ + else if (p1 === 'icon') { assetPromises.push((async () => { const assetDataBuf = await readImage(currentChar.image ?? '') multimodal.push({ @@ -930,11 +930,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) })()) } - return '' + return '' }) await Promise.all(assetPromises) - const chat:OpenAIChat = { + const chat: OpenAIChat = { role: role, content: formatedChat, memo: msg.chatId, @@ -942,7 +942,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ multimodals: multimodal, thoughts: thoughts } - if(chat.multimodals.length === 0){ + if (chat.multimodals.length === 0) { delete chat.multimodals } chats.push(chat) @@ -955,36 +955,36 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return (v.pos === 'depth' && v.depth > 0) || v.pos === 'reverse_depth' }) - for(const depthPrompt of depthPrompts){ - const chat:OpenAIChat = { + for (const depthPrompt of depthPrompts) { + const chat: OpenAIChat = { role: depthPrompt.role, - content: risuChatParser(depthPrompt.prompt, {chara: currentChar}) + content: risuChatParser(depthPrompt.prompt, { chara: currentChar }) } currentTokens += await tokenizer.tokenizeChat(chat) } - - if(nowChatroom.supaMemory && (DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypav2 || DBState.db.hypaV3)){ + + if (nowChatroom.supaMemory && (DBState.db.supaModelType !== 'none' || DBState.db.hanuraiEnable || DBState.db.hypav2 || DBState.db.hypaV3)) { stageTimings.stage1Duration = Date.now() - stageTimings.stage1Start chatProcessStage.set(2) stageTimings.stage2Start = Date.now() - if(DBState.db.hanuraiEnable){ + if (DBState.db.hanuraiEnable) { const hn = await hanuraiMemory(chats, { currentTokens, maxContextTokens, tokenizer }) - if(hn === false){ + if (hn === false) { return false } chats = hn.chats currentTokens = hn.tokens } - else if(DBState.db.hypav2){ + else if (DBState.db.hypav2) { console.log("Current chat's hypaV2 Data: ", currentChat.hypaV2Data) const sp = await hypaMemoryV2(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer) - if(sp.error){ + if (sp.error) { console.log(sp) throwError(sp.error) return false @@ -997,10 +997,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{ currentChat = DBState.db.characters[selectedChar].chats[selectedChat]; console.log("[Expected to be updated] chat's HypaV2Data: ", currentChat.hypaV2Data) } - else if(DBState.db.hypaV3){ + else if (DBState.db.hypaV3) { console.log("Current chat's hypaV3 Data: ", currentChat.hypaV3Data) const sp = await hypaMemoryV3(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer) - if(sp.error){ + if (sp.error) { // Save new summary if (sp.memory) { currentChat.hypaV3Data = sp.memory @@ -1014,15 +1014,15 @@ export async function sendChat(chatProcessIndex = -1,arg:{ currentTokens = sp.currentTokens currentChat.hypaV3Data = sp.memory ?? currentChat.hypaV3Data DBState.db.characters[selectedChar].chats[selectedChat].hypaV3Data = currentChat.hypaV3Data - + currentChat = DBState.db.characters[selectedChar].chats[selectedChat]; console.log("[Expected to be updated] chat's HypaV3Data: ", currentChat.hypaV3Data) } - else{ + else { const sp = await supaMemory(chats, currentTokens, maxContextTokens, currentChat, nowChatroom, tokenizer, { asHyper: DBState.db.hypaMemory }) - if(sp.error){ + if (sp.error) { throwError(sp.error) return false } @@ -1036,10 +1036,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{ stageTimings.stage2Duration = Date.now() - stageTimings.stage2Start chatProcessStage.set(1) } - else{ + else { stageTimings.stage1Duration = Date.now() - stageTimings.stage1Start - while(currentTokens > maxContextTokens){ - if(chats.length <= 1){ + while (currentTokens > maxContextTokens) { + if (chats.length <= 1) { throwError(language.errors.toomuchtoken + "\n\nRequired Tokens: " + currentTokens) return false @@ -1051,31 +1051,31 @@ export async function sendChat(chatProcessIndex = -1,arg:{ currentChat.lastMemory = chats[0].memo } - let biases:[string,number][] = DBState.db.bias.concat(currentChar.bias).map((v) => { - return [risuChatParser(v[0].replaceAll("\\n","\n").replaceAll("\\r","\r").replaceAll("\\\\","\\"), {chara: currentChar}),v[1]] + let biases: [string, number][] = DBState.db.bias.concat(currentChar.bias).map((v) => { + return [risuChatParser(v[0].replaceAll("\\n", "\n").replaceAll("\\r", "\r").replaceAll("\\\\", "\\"), { chara: currentChar }), v[1]] }) - let memories:OpenAIChat[] = [] + let memories: OpenAIChat[] = [] - if(!promptTemplate){ + if (!promptTemplate) { unformated.lastChat.push(chats[chats.length - 1]) chats.splice(chats.length - 1, 1) } unformated.chats = chats.map((v) => { - if(v.memo !== 'supaMemory' && v.memo !== 'hypaMemory'){ + if (v.memo !== 'supaMemory' && v.memo !== 'hypaMemory') { v.removable = true } - else if(supaMemoryCardUsed){ + else if (supaMemoryCardUsed) { memories.push(v) return { role: 'system', content: '', } as const } - else{ + else { v.content = `${v.content}` } return v @@ -1083,29 +1083,29 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return v.content !== '' }) - for(const depthPrompt of depthPrompts){ - const chat:OpenAIChat = { + for (const depthPrompt of depthPrompts) { + const chat: OpenAIChat = { role: depthPrompt.role, - content: risuChatParser(depthPrompt.prompt, {chara: currentChar}) + content: risuChatParser(depthPrompt.prompt, { chara: currentChar }) } const depth = depthPrompt.pos === 'depth' ? (depthPrompt.depth) : (unformated.chats.length - depthPrompt.depth) - unformated.chats.splice(depth,0,chat) + unformated.chats.splice(depth, 0, chat) } - if(triggerResult){ - if(triggerResult.additonalSysPrompt.promptend){ + if (triggerResult) { + if (triggerResult.additonalSysPrompt.promptend) { unformated.postEverything.push({ role: 'system', content: triggerResult.additonalSysPrompt.promptend }) } - if(triggerResult.additonalSysPrompt.historyend){ + if (triggerResult.additonalSysPrompt.historyend) { unformated.lastChat.push({ role: 'system', content: triggerResult.additonalSysPrompt.historyend }) } - if(triggerResult.additonalSysPrompt.start){ + if (triggerResult.additonalSysPrompt.start) { unformated.lastChat.unshift({ role: 'system', content: triggerResult.additonalSysPrompt.start @@ -1113,43 +1113,43 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } - + //make into one - let formated:OpenAIChat[] = [] + let formated: OpenAIChat[] = [] const formatOrder = safeStructuredClone(DBState.db.formatingOrder) - if(formatOrder){ + if (formatOrder) { formatOrder.push('postEverything') } //continue chat model - if(arg.continue && (DBState.db.aiModel.startsWith('claude') || DBState.db.aiModel.startsWith('gpt') || DBState.db.aiModel.startsWith('openrouter') || DBState.db.aiModel.startsWith('reverse_proxy'))){ + if (arg.continue && (DBState.db.aiModel.startsWith('claude') || DBState.db.aiModel.startsWith('gpt') || DBState.db.aiModel.startsWith('openrouter') || DBState.db.aiModel.startsWith('reverse_proxy'))) { unformated.postEverything.push({ role: 'system', content: '[Continue the last response]' }) } - function pushPrompts(cha:OpenAIChat[]){ - for(const chat of cha){ - if(!chat.content.trim()){ + function pushPrompts(cha: OpenAIChat[]) { + for (const chat of cha) { + if (!chat.content.trim()) { continue } - if(!(DBState.db.aiModel.startsWith('gpt') || DBState.db.aiModel.startsWith('claude') || DBState.db.aiModel === 'openrouter' || DBState.db.aiModel === 'reverse_proxy')){ + if (!(DBState.db.aiModel.startsWith('gpt') || DBState.db.aiModel.startsWith('claude') || DBState.db.aiModel === 'openrouter' || DBState.db.aiModel === 'reverse_proxy')) { formated.push(chat) continue } - if(chat.role === 'system'){ + if (chat.role === 'system') { const endf = formated.at(-1) - if(endf && endf.role === 'system' && endf.memo === chat.memo && endf.name === chat.name){ + if (endf && endf.role === 'system' && endf.memo === chat.memo && endf.name === chat.name) { formated[formated.length - 1].content += '\n\n' + chat.content } - else{ + else { formated.push(chat) } formated.at(-1).content += '' } - else{ + else { formated.push(chat) } } @@ -1157,7 +1157,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ let promptBodyformatedForChatStore: OpenAIChat[] = [] function pushPromptInfoBody(role: "function" | "system" | "user" | "assistant", fmt: string, promptBody: OpenAIChat[]) { - if(!fmt.trim()){ + if (!fmt.trim()) { return } promptBody.push({ @@ -1166,18 +1166,18 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) } - if(promptTemplate){ + if (promptTemplate) { const template = promptTemplate - for(const card of template){ - switch(card.type){ - case 'persona':{ + for (const card of template) { + switch (card.type) { + case 'persona': { let pmt = safeStructuredClone(unformated.personaPrompt) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content) - if(DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat){ + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat) { pushPromptInfoBody(pmt[i].role, card.innerFormat, promptBodyformatedForChatStore) } } @@ -1186,13 +1186,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ pushPrompts(pmt) break } - case 'description':{ + case 'description': { let pmt = safeStructuredClone(unformated.description) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content) + + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat) { pushPromptInfoBody(pmt[i].role, card.innerFormat, promptBodyformatedForChatStore) } } @@ -1201,13 +1201,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ pushPrompts(pmt) break } - case 'authornote':{ + case 'authornote': { let pmt = safeStructuredClone(unformated.authorNote) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(positionParser(card.innerFormat, card.type), { chara: currentChar }).replace('{{slot}}', pmt[i].content || card.defaultText || '') + + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat) { pushPromptInfoBody(pmt[i].role, card.innerFormat, promptBodyformatedForChatStore) } } @@ -1216,13 +1216,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ pushPrompts(pmt) break } - case 'lorebook':{ + case 'lorebook': { pushPrompts(unformated.lorebook) break } - case 'postEverything':{ + case 'postEverything': { pushPrompts(unformated.postEverything) - if(usingPromptTemplate && DBState.db.promptSettings.postEndInnerFormat){ + if (usingPromptTemplate && DBState.db.promptSettings.postEndInnerFormat) { pushPrompts([{ role: 'system', content: DBState.db.promptSettings.postEndInnerFormat @@ -1232,11 +1232,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } case 'plain': case 'jailbreak': - case 'cot':{ - if((!DBState.db.jailbreakToggle) && (card.type === 'jailbreak')){ + case 'cot': { + if ((!DBState.db.jailbreakToggle) && (card.type === 'jailbreak')) { continue } - if((!DBState.db.chainOfThought) && (card.type === 'cot')){ + if ((!DBState.db.chainOfThought) && (card.type === 'cot')) { continue } @@ -1249,77 +1249,77 @@ export async function sendChat(chatProcessIndex = -1,arg:{ const posType = card.type === 'plain' ? card.type2 : card.type let content = positionParser(card.text, posType) - if(card.type2 === 'globalNote'){ - if(currentChar.replaceGlobalNote){ + if (card.type2 === 'globalNote') { + if (currentChar.replaceGlobalNote) { content = positionParser(currentChar.replaceGlobalNote, posType).replaceAll('{{original}}', content) } - if(currentChar.prebuiltAssetCommand && !card.text.includes('{{//@customimageinstruction}}')){ + if (currentChar.prebuiltAssetCommand && !card.text.includes('{{//@customimageinstruction}}')) { content += prebuiltAssetCommand } - content = (risuChatParser(content, {chara: currentChar, role: card.role})) + content = (risuChatParser(content, { chara: currentChar, role: card.role })) } - else if(card.type2 === 'main'){ - content = (risuChatParser(content, {chara: currentChar, role: card.role})) + else if (card.type2 === 'main') { + content = (risuChatParser(content, { chara: currentChar, role: card.role })) } - else{ - content = risuChatParser(content, {chara: currentChar, role: card.role}) + else { + content = risuChatParser(content, { chara: currentChar, role: card.role }) } - const prompt:OpenAIChat ={ + const prompt: OpenAIChat = { role: convertRole[card.role], content: content } - if(DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat && card.type2 !== 'globalNote'){ + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat && card.type2 !== 'globalNote') { pushPromptInfoBody(prompt.role, prompt.content, promptBodyformatedForChatStore) } pushPrompts([prompt]) break } - case 'chatML':{ + case 'chatML': { let prompts = parseChatML(card.text) pushPrompts(prompts) break } - case 'chat':{ + case 'chat': { let start = card.rangeStart let end = (card.rangeEnd === 'end') ? unformated.chats.length : card.rangeEnd - if(start === -1000){ + if (start === -1000) { start = 0 end = unformated.chats.length } - if(start < 0){ + if (start < 0) { start = unformated.chats.length + start - if(start < 0){ + if (start < 0) { start = 0 } } - if(end < 0){ + if (end < 0) { end = unformated.chats.length + end - if(end < 0){ + if (end < 0) { end = 0 } } - - if(start >= end){ + + if (start >= end) { break } let chats = unformated.chats.slice(start, end) - if(usingPromptTemplate && DBState.db.promptSettings.sendChatAsSystem && (!card.chatAsOriginalOnSystem)){ + if (usingPromptTemplate && DBState.db.promptSettings.sendChatAsSystem && (!card.chatAsOriginalOnSystem)) { chats = systemizeChat(chats) } pushPrompts(chats) - if(DBState.db.automaticCachePoint && !hasCachePoint){ + if (DBState.db.automaticCachePoint && !hasCachePoint) { let pointer = formated.length - 1 let depthRemaining = 3 - while(pointer >= 0){ - if(depthRemaining === 0){ + while (pointer >= 0) { + if (depthRemaining === 0) { break } - if(formated[pointer].role === 'user'){ + if (formated[pointer].role === 'user') { formated[pointer].cachePoint = true depthRemaining-- } @@ -1328,13 +1328,13 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } break } - case 'memory':{ + case 'memory': { let pmt = safeStructuredClone(memories) - if(card.innerFormat && pmt.length > 0){ - for(let i=0;i 0) { + for (let i = 0; i < pmt.length; i++) { + pmt[i].content = risuChatParser(card.innerFormat, { chara: currentChar }).replace('{{slot}}', pmt[i].content) - if(DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat){ + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat) { pushPromptInfoBody(pmt[i].role, card.innerFormat, promptBodyformatedForChatStore) } } @@ -1343,14 +1343,14 @@ export async function sendChat(chatProcessIndex = -1,arg:{ pushPrompts(pmt) break } - case 'cache':{ + case 'cache': { let pointer = formated.length - 1 let depthRemaining = card.depth - while(pointer >= 0){ - if(depthRemaining === 0){ + while (pointer >= 0) { + if (depthRemaining === 0) { break } - if(formated[pointer].role === card.role || card.role === 'all'){ + if (formated[pointer].role === card.role || card.role === 'all') { formated[pointer].cachePoint = true depthRemaining-- } @@ -1361,8 +1361,8 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } } - else{ - for(let i=0;i { v.content = v.content.trim() return v @@ -1382,18 +1382,18 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } - if(currentChar.depth_prompt && currentChar.depth_prompt.prompt && currentChar.depth_prompt.prompt.length > 0){ + if (currentChar.depth_prompt && currentChar.depth_prompt.prompt && currentChar.depth_prompt.prompt.length > 0) { //depth_prompt const depthPrompt = currentChar.depth_prompt formated.splice(formated.length - depthPrompt.depth, 0, { role: 'system', - content: risuChatParser(depthPrompt.prompt, {chara: currentChar}) + content: risuChatParser(depthPrompt.prompt, { chara: currentChar }) }) } formated = await runLuaEditTrigger(currentChar, 'editRequest', formated) - if(DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat){ + if (DBState.db.promptInfoInsideChat && DBState.db.promptTextInfoInsideChat) { promptBodyformatedForChatStore = await runLuaEditTrigger(currentChar, 'editRequest', promptBodyformatedForChatStore) promptInfo.promptText = promptBodyformatedForChatStore } @@ -1401,37 +1401,37 @@ export async function sendChat(chatProcessIndex = -1,arg:{ //token rechecking let inputTokens = 0 - for(const chat of formated){ + for (const chat of formated) { inputTokens += await tokenizer.tokenizeChat(chat) } - if(inputTokens > maxContextTokens){ + if (inputTokens > maxContextTokens) { let pointer = 0 - while(inputTokens > maxContextTokens){ - if(pointer >= formated.length){ + while (inputTokens > maxContextTokens) { + if (pointer >= formated.length) { throwError(language.errors.toomuchtoken + "\n\nAt token rechecking. Required Tokens: " + inputTokens) return false } - if(formated[pointer].removable){ + if (formated[pointer].removable) { inputTokens -= await tokenizer.tokenizeChat(formated[pointer]) formated[pointer].content = '' } pointer++ } formated = formated.filter((v) => { - return v.content !== '' || (v.multimodals && v.multimodals.length > 0) + return v.content !== '' || (v.multimodals && v.multimodals.length > 0) }) } //estimate tokens let outputTokens = DBState.db.maxResponse - if(inputTokens + outputTokens > maxContextTokens){ + if (inputTokens + outputTokens > maxContextTokens) { outputTokens = maxContextTokens - inputTokens } const generationId = v4() const generationModel = getGenerationModelString() - const generationInfo:MessageGenerationInfo = { + const generationInfo: MessageGenerationInfo = { model: generationModel, generationId: generationId, inputTokens: inputTokens, @@ -1447,7 +1447,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ chatProcessStage.set(3) stageTimings.stage3Start = Date.now() - if(arg.preview){ + if (arg.preview) { previewFormated = formated return true } @@ -1468,12 +1468,12 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }, 'model', abortSignal) console.log(req) - if(req.model){ + if (req.model) { generationInfo.model = getGenerationModelString(req.model) console.log(generationInfo.model, req.model) } - if(arg.previewPrompt && req.type === 'success'){ + if (arg.previewPrompt && req.type === 'success') { previewBody = req.result return true } @@ -1481,23 +1481,23 @@ export async function sendChat(chatProcessIndex = -1,arg:{ let result = '' let emoChanged = false let resendChat = false - - if(abortSignal.aborted === true){ + + if (abortSignal.aborted === true) { return false } - if(req.type === 'fail'){ + if (req.type === 'fail') { throwError(req.result) return false } - else if(req.type === 'streaming'){ + else if (req.type === 'streaming') { const reader = req.result.getReader() let msgIndex = DBState.db.characters[selectedChar].chats[selectedChat].message.length let prefix = '' - if(arg.continue){ + if (arg.continue) { msgIndex -= 1 prefix = DBState.db.characters[selectedChar].chats[selectedChat].message[msgIndex].data } - else{ + else { DBState.db.characters[selectedChar].chats[selectedChat].message.push({ role: 'char', data: "", @@ -1509,17 +1509,17 @@ export async function sendChat(chatProcessIndex = -1,arg:{ }) } DBState.db.characters[selectedChar].chats[selectedChat].isStreaming = true - let lastResponseChunk:{[key:string]:string} = {} - while(abortSignal.aborted === false){ + let lastResponseChunk: { [key: string]: string } = {} + while (abortSignal.aborted === false) { const readed = (await reader.read()) - if(readed.value){ + if (readed.value) { lastResponseChunk = readed.value const firstChunkKey = Object.keys(lastResponseChunk)[0] result = lastResponseChunk[firstChunkKey] - if(!result){ + if (!result) { result = '' } - if(DBState.db.removeIncompleteResponse){ + if (DBState.db.removeIncompleteResponse) { result = trimUntilPunctuation(result) } let result2 = await processScriptFull(nowChatroom, reformatContent(prefix + result), 'editoutput', msgIndex) @@ -1527,59 +1527,59 @@ export async function sendChat(chatProcessIndex = -1,arg:{ emoChanged = result2.emoChanged DBState.db.characters[selectedChar].reloadKeys += 1 } - if(readed.done){ + if (readed.done) { DBState.db.characters[selectedChar].chats[selectedChat].isStreaming = false DBState.db.characters[selectedChar].reloadKeys += 1 break - } + } } addRerolls(generationId, Object.values(lastResponseChunk)) DBState.db.characters[selectedChar].chats[selectedChat] = runCurrentChatFunction(DBState.db.characters[selectedChar].chats[selectedChat]) - currentChat = DBState.db.characters[selectedChar].chats[selectedChat] - const triggerResult = await runTrigger(currentChar, 'output', {chat:currentChat}) - if(triggerResult && triggerResult.chat){ + currentChat = DBState.db.characters[selectedChar].chats[selectedChat] + const triggerResult = await runTrigger(currentChar, 'output', { chat: currentChat }) + if (triggerResult && triggerResult.chat) { currentChat = triggerResult.chat } - if(triggerResult && triggerResult.sendAIprompt){ + if (triggerResult && triggerResult.sendAIprompt) { resendChat = true } const inlayr = runInlayScreen(currentChar, currentChat.message[msgIndex].data) currentChat.message[msgIndex].data = inlayr.text DBState.db.characters[selectedChar].chats[selectedChat] = currentChat - if(inlayr.promise){ + if (inlayr.promise) { const t = await inlayr.promise currentChat.message[msgIndex].data = t DBState.db.characters[selectedChar].chats[selectedChat] = currentChat } - if(DBState.db.ttsAutoSpeech){ + if (DBState.db.ttsAutoSpeech) { await sayTTS(currentChar, result) } } - else{ - const msgs = (req.type === 'success') ? [['char',req.result]] as const - : (req.type === 'multiline') ? req.result - : [] - let mrerolls:string[] = [] - for(let i=0;i1){ + if (mrerolls.length > 1) { addRerolls(generationId, mrerolls) } DBState.db.characters[selectedChar].chats[selectedChat] = runCurrentChatFunction(DBState.db.characters[selectedChar].chats[selectedChat]) - currentChat = DBState.db.characters[selectedChar].chats[selectedChat] + currentChat = DBState.db.characters[selectedChar].chats[selectedChat] - const triggerResult = await runTrigger(currentChar, 'output', {chat:currentChat}) - if(triggerResult && triggerResult.chat){ + const triggerResult = await runTrigger(currentChar, 'output', { chat: currentChat }) + if (triggerResult && triggerResult.chat) { DBState.db.characters[selectedChar].chats[selectedChat] = triggerResult.chat } - if(triggerResult && triggerResult.sendAIprompt){ + if (triggerResult && triggerResult.sendAIprompt) { resendChat = true } } let needsAutoContinue = false const resultTokens = await tokenize(result) + (arg.usedContinueTokens || 0) - if(DBState.db.autoContinueMinTokens > 0 && resultTokens < DBState.db.autoContinueMinTokens){ + if (DBState.db.autoContinueMinTokens > 0 && resultTokens < DBState.db.autoContinueMinTokens) { needsAutoContinue = true } - if(DBState.db.autoContinueChat && (!isLastCharPunctuation(result))){ + if (DBState.db.autoContinueChat && (!isLastCharPunctuation(result))) { //if result doesn't end with punctuation or special characters, auto continue needsAutoContinue = true } - if(needsAutoContinue){ + if (needsAutoContinue) { doingChat.set(false) return await sendChat(chatProcessIndex, { chatAdditonalTokens: arg.chatAdditonalTokens, @@ -1659,49 +1659,49 @@ export async function sendChat(chatProcessIndex = -1,arg:{ const igp = risuChatParser(DBState.db.igpPrompt ?? "") - if(igp){ + if (igp) { const igpFormated = parseChatML(igp) const rq = await requestChatData({ formated: igpFormated, bias: {} - },'emotion', abortSignal) + }, 'emotion', abortSignal) DBState.db.characters[selectedChar].chats[selectedChat].message[DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1].data += rq } stageTimings.stage3Duration = Date.now() - stageTimings.stage3Start - if(generationInfo.stageTiming) { + if (generationInfo.stageTiming) { generationInfo.stageTiming.stage3 = stageTimings.stage3Duration } chatProcessStage.set(4) stageTimings.stage4Start = Date.now() - if(resendChat){ + if (resendChat) { stageTimings.stage4Duration = Date.now() - stageTimings.stage4Start - - if(generationInfo.stageTiming) { + + if (generationInfo.stageTiming) { generationInfo.stageTiming.stage1 = stageTimings.stage1Duration generationInfo.stageTiming.stage2 = stageTimings.stage2Duration generationInfo.stageTiming.stage3 = stageTimings.stage3Duration generationInfo.stageTiming.stage4 = stageTimings.stage4Duration } - + const lastMessageIndex = DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1 - if(lastMessageIndex >= 0 && DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo) { + if (lastMessageIndex >= 0 && DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo) { DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo = generationInfo } - + doingChat.set(false) return await sendChat(chatProcessIndex, { signal: abortSignal }) } - if(DBState.db.notification){ + if (DBState.db.notification) { try { const permission = await Notification.requestPermission() - if(permission === 'granted'){ + if (permission === 'granted') { const noti = new Notification('RisuAI', { body: result }) @@ -1710,28 +1710,28 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } } catch (error) { - + } } peerSync() - if(req.special){ - if(req.special.emotion){ + if (req.special) { + if (req.special.emotion) { let charemotions = get(CharEmotion) let currentEmotion = currentChar.emotionImages let tempEmotion = charemotions[currentChar.chaId] - if(!tempEmotion){ + if (!tempEmotion) { tempEmotion = [] } - if(tempEmotion.length > 4){ + if (tempEmotion.length > 4) { tempEmotion.splice(0, 1) } - for(const emo of currentEmotion){ - if(emo[0] === req.special.emotion){ - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + for (const emo of currentEmotion) { + if (emo[0] === req.special.emotion) { + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[currentChar.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -1742,8 +1742,8 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } - if(!currentChar.inlayViewScreen){ - if(currentChar.viewScreen === 'emotion' && (!emoChanged) && (abortSignal.aborted === false)){ + if (!currentChar.inlayViewScreen) { + if (currentChar.viewScreen === 'emotion' && (!emoChanged) && (abortSignal.aborted === false)) { let currentEmotion = currentChar.emotionImages let emotionList = currentEmotion.map((a) => { @@ -1752,23 +1752,23 @@ export async function sendChat(chatProcessIndex = -1,arg:{ let charemotions = get(CharEmotion) let tempEmotion = charemotions[currentChar.chaId] - if(!tempEmotion){ + if (!tempEmotion) { tempEmotion = [] } - if(tempEmotion.length > 4){ + if (tempEmotion.length > 4) { tempEmotion.splice(0, 1) } - if(DBState.db.emotionProcesser === 'embedding'){ + if (DBState.db.emotionProcesser === 'embedding') { const hypaProcesser = new HypaProcesser() await hypaProcesser.addText(emotionList.map((v) => 'emotion:' + v)) let searched = (await hypaProcesser.similaritySearchScored(result)).map((v) => { - v[0] = v[0].replace("emotion:",'') + v[0] = v[0].replace("emotion:", '') return v }) //give panaltys - for(let i =0;i { @@ -1777,21 +1777,21 @@ export async function sendChat(chatProcessIndex = -1,arg:{ const modifier = ((5 - ((tempEmotion.length - (i + 1))))) / 200 - if(index !== -1){ + if (index !== -1) { searched[index][1] -= modifier } } //make a sorted array by score - const emoresult = searched.sort((a,b) => { + const emoresult = searched.sort((a, b) => { return b[1] - a[1] }).map((v) => { return v[0] }) - for(const emo of currentEmotion){ - if(emo[0] === emoresult[0]){ - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + for (const emo of currentEmotion) { + if (emo[0] === emoresult[0]) { + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[currentChar.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -1799,12 +1799,12 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } - + return true } - function shuffleArray(array:string[]) { + function shuffleArray(array: string[]) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; @@ -1812,32 +1812,32 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return array } - let emobias:{[key:number]:number} = {} + let emobias: { [key: number]: number } = {} - for(const emo of emotionList){ + for (const emo of emotionList) { const tokens = await tokenizeNum(emo) - for(const token of tokens){ + for (const token of tokens) { emobias[token] = 10 } } - for(let i =0;i { return a[0] }) try { - const emotion:string = rq.result.replace(/ |\n/g,'').trim().toLocaleLowerCase() + const emotion: string = rq.result.replace(/ |\n/g, '').trim().toLocaleLowerCase() let emotionSelected = false - for(const emo of currentEmotion){ - if(emo[0] === emotion){ - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + for (const emo of currentEmotion) { + if (emo[0] === emotion) { + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[currentChar.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -1885,10 +1885,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{ break } } - if(!emotionSelected){ - for(const emo of currentEmotion){ - if(emotion.includes(emo[0])){ - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + if (!emotionSelected) { + for (const emo of currentEmotion) { + if (emotion.includes(emo[0])) { + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[currentChar.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -1897,9 +1897,9 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } } } - if(!emotionSelected && emotionList.includes('neutral')){ + if (!emotionSelected && emotionList.includes('neutral')) { const emo = currentEmotion[emotionList.indexOf('neutral')] - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[currentChar.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -1910,23 +1910,23 @@ export async function sendChat(chatProcessIndex = -1,arg:{ return true } } - + return true } - else if(currentChar.viewScreen === 'imggen'){ - if(chatProcessIndex !== -1){ + else if (currentChar.viewScreen === 'imggen') { + if (chatProcessIndex !== -1) { throwError("Stable diffusion in group chat is not supported") } const msgs = DBState.db.characters[selectedChar].chats[selectedChat].message let msgStr = '' - for(let i = (msgs.length - 1);i>=0;i--){ - if(msgs[i].role === 'char'){ + for (let i = (msgs.length - 1); i >= 0; i--) { + if (msgs[i].role === 'char') { msgStr = `character: ${msgs[i].data.replace(/\n/g, ' ')} \n` + msgStr } - else{ + else { msgStr = `user: ${msgs[i].data.replace(/\n/g, ' ')} \n` + msgStr break } @@ -1938,30 +1938,30 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } stageTimings.stage4Duration = Date.now() - stageTimings.stage4Start - - if(generationInfo.stageTiming) { + + if (generationInfo.stageTiming) { generationInfo.stageTiming.stage1 = stageTimings.stage1Duration generationInfo.stageTiming.stage2 = stageTimings.stage2Duration generationInfo.stageTiming.stage3 = stageTimings.stage3Duration generationInfo.stageTiming.stage4 = stageTimings.stage4Duration } - + const lastMessageIndex = DBState.db.characters[selectedChar].chats[selectedChat].message.length - 1 - if(lastMessageIndex >= 0 && DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo) { + if (lastMessageIndex >= 0 && DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo) { DBState.db.characters[selectedChar].chats[selectedChat].message[lastMessageIndex].generationInfo = generationInfo } return true } -function systemizeChat(chat:OpenAIChat[]){ - for(let i=0;i() +let ScriptingEditDisplayIds = new Set() +let ScriptingLowLevelIds = new Set() + +export function getAccessKeys() { + return { ScriptingSafeIds, ScriptingEditDisplayIds, ScriptingLowLevelIds } +} + +export function declareAPI( + declare: (name: string, func: Function) => void, + ScriptingEngineState: ScriptingEngineState, + stopSending: () => void, + char: character | groupChat | simpleCharacterArgument +) { + declare('getChatVar', (id: string, key: string) => { + return ScriptingEngineState.getVar(key) + }) + declare('setChatVar', (id: string, key: string, value: string) => { + if (!ScriptingSafeIds.has(id) && !ScriptingEditDisplayIds.has(id)) { + return + } + ScriptingEngineState.setVar(key, value) + }) + declare('getGlobalVar', (id: string, key: string) => { + return getGlobalChatVar(key) + }) + declare('stopChat', (id: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + stopSending() + }) + declare('alertError', (id: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + alertError(value) + }) + declare('alertNormal', (id: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + alertNormal(value) + }) + declare('alertInput', (id: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + return alertInput(value) + }) + declare('alertSelect', (id: string, value: string[]) => { + if (!ScriptingSafeIds.has(id)) { + return + } + return alertSelect(value) + }) + + declare('getChatMain', (id: string, index: number) => { + const chat = ScriptingEngineState.chat.message.at(index) + if (!chat) { + return JSON.stringify(null) + } + const data = { + role: chat.role, + data: chat.data, + time: chat.time ?? 0 + } + return JSON.stringify(data) + }) + + declare('setChat', (id: string, index: number, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const message = ScriptingEngineState.chat.message?.at(index) + if (message) { + message.data = value ?? '' + } + }) + declare('setChatRole', (id: string, index: number, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const message = ScriptingEngineState.chat.message?.at(index) + if (message) { + message.role = value === 'user' ? 'user' : 'char' + } + }) + declare('cutChat', (id: string, start: number, end: number) => { + if (!ScriptingSafeIds.has(id)) { + return + } + ScriptingEngineState.chat.message = ScriptingEngineState.chat.message.slice(start, end) + }) + declare('removeChat', (id: string, index: number) => { + if (!ScriptingSafeIds.has(id)) { + return + } + ScriptingEngineState.chat.message.splice(index, 1) + }) + declare('addChat', (id: string, role: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + let roleData: 'user' | 'char' = role === 'user' ? 'user' : 'char' + ScriptingEngineState.chat.message.push({ role: roleData, data: value ?? '' }) + }) + declare('insertChat', (id: string, index: number, role: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + let roleData: 'user' | 'char' = role === 'user' ? 'user' : 'char' + ScriptingEngineState.chat.message.splice(index, 0, { role: roleData, data: value ?? '' }) + }) + + declare('getTokens', async (id: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + return await tokenize(value) + }) + + declare('getChatLength', (id: string) => { + return ScriptingEngineState.chat.message.length + }) + + declare('getFullChatMain', (id: string) => { + const data = JSON.stringify(ScriptingEngineState.chat.message.map((v) => { + return { + role: v.role, + data: v.data, + time: v.time ?? 0 + } + })) + return data + }) + + declare('cbs', (value) => { + return risuChatParser(value, { chara: getCurrentCharacter() }) + }) + + declare('setFullChatMain', (id: string, value: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const realValue = JSON.parse(value) + + ScriptingEngineState.chat.message = realValue.map((v) => { + return { + role: v.role, + data: v.data + } + }) + }) + + declare('logMain', (value: string) => { + console.log(JSON.parse(value)) + }) + + declare('reloadDisplay', (id: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) + }) + + declare('reloadChat', (id: string, index: number) => { + if (!ScriptingSafeIds.has(id)) { + return + } + ReloadChatPointer.update((v) => { + v[index] = (v[index] ?? 0) + 1 + return v + }) + }) + + //Low Level Access + declare('similarity', async (id: string, source: string, value: string[]) => { + if (!ScriptingLowLevelIds.has(id)) { + return + } + const processer = new HypaProcesser() + await processer.addText(value) + return await processer.similaritySearch(source) + }) + + declare('request', async (id: string, url: string) => { + if (!ScriptingLowLevelIds.has(id)) { + return + } + + if (lastRequestResetTime + 60000 < Date.now()) { + lastRequestsCount = 0 + lastRequestResetTime = Date.now() + } + + if (lastRequestsCount > 5) { + return JSON.stringify({ + status: 429, + data: 'Too many requests. you can request 5 times per minute' + }) + } + + lastRequestsCount++ + + try { + //for security and other reasons, only get request in 120 char is allowed + if (url.length > 120) { + return JSON.stringify({ + status: 413, + data: 'URL to large. max is 120 characters' + }) + } + + if (!url.startsWith('https://')) { + return JSON.stringify({ + status: 400, + data: "Only https requests are allowed" + }) + } + + const bannedURL = [ + "https://realm.risuai.net", + "https://risuai.net", + "https://risuai.xyz" + ] + + for (const burl of bannedURL) { + + if (url.startsWith(burl)) { + return JSON.stringify({ + status: 400, + data: "request to " + url + ' is not allowed' + }) + } + } + + //browser fetch + const d = await fetchNative(url, { + method: "GET" + }) + const text = await d.text() + return JSON.stringify({ + status: d.status, + data: text + }) + + } catch (error) { + return JSON.stringify({ + status: 400, + data: 'internal error' + }) + } + }) + + declare('generateImage', async (id: string, value: string, negValue: string = '') => { + if (!ScriptingLowLevelIds.has(id)) { + return + } + const gen = await generateAIImage(value, char as character, negValue, 'inlay') + if (!gen) { + return 'Error: Image generation failed' + } + const imgHTML = new Image() + imgHTML.src = gen + const inlay = await writeInlayImage(imgHTML) + return `{{inlay::${inlay}}}` + }) + + declare('hash', async (id: string, value: string) => { + return await hasher(new TextEncoder().encode(value)) + }) + + declare('LLMMain', async (id: string, promptStr: string) => { + let prompt: { + role: string, + content: string + }[] = JSON.parse(promptStr) + if (!ScriptingLowLevelIds.has(id)) { + return + } + let promptbody: OpenAIChat[] = prompt.map((dict) => { + let role: 'system' | 'user' | 'assistant' = 'assistant' + switch (dict['role']) { + case 'system': + case 'sys': + role = 'system' + break + case 'user': + role = 'user' + break + case 'assistant': + case 'bot': + case 'char': { + role = 'assistant' + break + } + } + + return { + content: dict['content'] ?? '', + role: role, + } + }) + const result = await requestChatData({ + formated: promptbody, + bias: {}, + useStreaming: false, + noMultiGen: true, + }, 'model') + + if (result.type === 'fail') { + return JSON.stringify({ + success: false, + result: 'Error: ' + result.result + }) + } + + if (result.type === 'streaming' || result.type === 'multiline') { + return JSON.stringify({ + success: false, + result: result.result + }) + } + + return JSON.stringify({ + success: true, + result: result.result + }) + }) + + declare('simpleLLM', async (id: string, prompt: string) => { + if (!ScriptingLowLevelIds.has(id)) { + return + } + const result = await requestChatData({ + formated: [{ + role: 'user', + content: prompt + }], + bias: {}, + useStreaming: false, + noMultiGen: true, + }, 'model') + + if (result.type === 'fail') { + return { + success: false, + result: 'Error: ' + result.result + } + } + + if (result.type === 'streaming' || result.type === 'multiline') { + return { + success: false, + result: result.result + } + } + + return { + success: true, + result: result.result + } + }) + + declare('getName', async (id: string) => { + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.name + }) + + declare('setName', async (id: string, name: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + if (typeof name !== 'string') { + throw ('Invalid data type') + } + db.characters[selectedChar].name = name + setDatabase(db) + }) + + declare('getDescription', async (id: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + if (char.type === 'group') { + throw ('Character is a group') + } + return char.desc + }) + + declare('setDescription', async (id: string, desc: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + if (typeof desc !== 'string') { + throw ('Invalid data type') + } + if (char.type === 'group') { + throw ('Character is a group') + } + char.desc = desc + db.characters[selectedChar] = char + setDatabase(db) + }) + + declare('getCharacterFirstMessage', async (id: string) => { + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.firstMessage + }) + + declare('setCharacterFirstMessage', async (id: string, data: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + if (typeof data !== 'string') { + return false + } + char.firstMessage = data + db.characters[selectedChar] = char + setDatabase(db) + return true + }) + + declare('getPersonaName', (id: string) => { + return getUserName() + }) + + declare('getPersonaDescription', (id: string) => { + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + + return risuChatParser(getPersonaPrompt(), { chara: char }) + }) + + declare('getAuthorsNote', (id: string) => { + return ScriptingEngineState.chat?.note ?? '' + }) + + declare('getBackgroundEmbedding', async (id: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.backgroundHTML + }) + + declare('setBackgroundEmbedding', async (id: string, data: string) => { + if (!ScriptingSafeIds.has(id)) { + return + } + const db = getDatabase() + const selectedChar = get(selectedCharID) + if (typeof data !== 'string') { + return false + } + db.characters[selectedChar].backgroundHTML = data + setDatabase(db) + return true + }) + + // Lore books + declare('getLoreBooksMain', (id: string, search: string) => { + const db = getDatabase() + const selectedChar = db.characters[get(selectedCharID)] + if (selectedChar.type !== 'character') { + return + } + + const loreBooks = [...selectedChar.chats[selectedChar.chatPage]?.localLore ?? [], ...selectedChar.globalLore, ...getModuleLorebooks()] + const found = loreBooks.filter((b) => b.comment === search) + + return JSON.stringify(found.map((b) => ({ ...b, content: risuChatParser(b.content, { chara: selectedChar }) }))) + }) + + type upsertLoreBookOptions = { + alwaysActive?: boolean + insertOrder?: number + key?: string + secondKey?: string + regex?: boolean + } + + declare('upsertLocalLoreBook', (id: string, name: string, content: string, options: upsertLoreBookOptions) => { + if (!ScriptingSafeIds.has(id)) { + return + } + + if (char.type !== 'character') { + return + } + + const { + alwaysActive = false, + insertOrder = 100, + key = '', + regex = false, + secondKey = '', + } = options + + const currentChat = char.chats[char.chatPage] + + const newLocalLoreBooks = currentChat.localLore.filter((book) => book.comment !== name) + newLocalLoreBooks.push({ + alwaysActive, + comment: name, + content: content, + insertorder: insertOrder, + mode: 'normal', + key, + secondkey: secondKey, + selective: !!secondKey, + useRegex: regex, + }) + currentChat.localLore = newLocalLoreBooks + }) + + declare('loadLoreBooksMain', async (id: string, reserve: number) => { + if (!ScriptingLowLevelIds.has(id)) { + return + } + + const db = getDatabase() + + const selectedChar = db.characters[get(selectedCharID)] + + if (selectedChar.type !== 'character') { + return + } + + const fullLoreBooks = (await loadLoreBookV3Prompt()).actives + const maxContext = db.maxContext - reserve + if (maxContext < 0) { + return JSON.stringify([]) + } + + let totalTokens = 0 + const loreBooks = [] + + for (const book of fullLoreBooks) { + const parsed = risuChatParser(book.prompt, { chara: selectedChar }).trim() + if (parsed.length === 0) { + continue + } + + const tokens = await tokenize(parsed) + + if (totalTokens + tokens > maxContext) { + break + } + totalTokens += tokens + loreBooks.push({ + data: parsed, + role: book.role === 'assistant' ? 'char' : book.role, + }) + } + + return JSON.stringify(loreBooks) + }) + + declare('axLLMMain', async (id: string, promptStr: string) => { + let prompt: { + role: string, + content: string + }[] = JSON.parse(promptStr) + if (!ScriptingLowLevelIds.has(id)) { + return + } + let promptbody: OpenAIChat[] = prompt.map((dict) => { + let role: 'system' | 'user' | 'assistant' = 'assistant' + switch (dict['role']) { + case 'system': + case 'sys': + role = 'system' + break + case 'user': + role = 'user' + break + case 'assistant': + case 'bot': + case 'char': { + role = 'assistant' + break + } + } + + return { + content: dict['content'] ?? '', + role: role, + } + }) + const result = await requestChatData({ + formated: promptbody, + bias: {}, + useStreaming: false, + noMultiGen: true, + }, 'otherAx') + + if (result.type === 'fail') { + return JSON.stringify({ + success: false, + result: 'Error: ' + result.result + }) + } + + if (result.type === 'streaming' || result.type === 'multiline') { + return JSON.stringify({ + success: false, + result: result.result + }) + } + + return JSON.stringify({ + success: true, + result: result.result + }) + }) + + declare('getCharacterLastMessage', (id: string) => { + const chat = ScriptingEngineState.chat + if (!chat) { + return '' + } + + const db = getDatabase() + const selchar = db.characters[get(selectedCharID)] + + let pointer = chat.message.length - 1 + while (pointer >= 0) { + if (chat.message[pointer].role === 'char') { + const messageData = chat.message[pointer].data + return messageData + } + pointer-- + } + + return selchar.firstMessage + }) + + declare('getUserLastMessage', (id: string) => { + const chat = ScriptingEngineState.chat + if (!chat) { + return null + } + + let pointer = chat.message.length - 1 + while (pointer >= 0) { + if (chat.message[pointer].role === 'user') { + const messageData = chat.message[pointer].data + return messageData + } + pointer-- + } + + return null + }) +} diff --git a/src/ts/process/script/engine/index.ts b/src/ts/process/script/engine/index.ts new file mode 100644 index 0000000000..c84d302761 --- /dev/null +++ b/src/ts/process/script/engine/index.ts @@ -0,0 +1,58 @@ +import { Mutex } from "src/ts/mutex"; +import type { Chat } from "src/ts/storage/database.svelte"; +import type { LuaEngine } from "wasmoon"; +import type { PyodideContext } from "./python"; + +export interface BasicScriptingEngineState { + code?: string; + mutex: Mutex; + chat?: Chat; + setVar?: (key: string, value: string) => void, + getVar?: (key: string) => string, +} + +export interface LuaScriptingEngineState extends BasicScriptingEngineState { + engine?: LuaEngine; + type: 'lua'; +} + +export interface PythonScriptingEngineState extends BasicScriptingEngineState { + pyodide?: PyodideContext + type: 'py'; +} + +export type ScriptingEngineState = LuaScriptingEngineState | PythonScriptingEngineState; + +export let ScriptingEngines = new Map() +let pendingEngineCreations = new Map>(); + +export async function getOrCreateEngineState( + mode: string, + type: 'lua' | 'py' +): Promise { + let engineState = ScriptingEngines.get(mode); + if (engineState) { + return engineState; + } + + let pendingCreation = pendingEngineCreations.get(mode); + if (pendingCreation) { + return pendingCreation; + } + + const creationPromise = (async () => { + const engineState: ScriptingEngineState = { + mutex: new Mutex(), + type: type, + }; + ScriptingEngines.set(mode, engineState); + + pendingEngineCreations.delete(mode); + + return engineState; + })(); + + pendingEngineCreations.set(mode, creationPromise); + + return creationPromise; +} diff --git a/src/ts/process/script/engine/lua.ts b/src/ts/process/script/engine/lua.ts new file mode 100644 index 0000000000..1d1c9a157d --- /dev/null +++ b/src/ts/process/script/engine/lua.ts @@ -0,0 +1,254 @@ +import { LuaEngine, LuaFactory } from "wasmoon"; +import type { LuaScriptingEngineState } from "."; + +export let luaFactory: LuaFactory + +let luaFactoryPromise: Promise | null = null; + +async function makeLuaFactory() { + const _luaFactory = new LuaFactory() + async function mountFile(name: string) { + let code = '' + for (let i = 0; i < 3; i++) { + try { + const res = await fetch('/lua/' + name) + if (res.status >= 200 && res.status < 300) { + code = await res.text() + break + } + } catch (error) { } + } + await _luaFactory.mountFile(name, code) + } + + await mountFile('json.lua') + luaFactory = _luaFactory +} + +export async function ensureLuaFactory() { + if (luaFactory) return; + + if (luaFactoryPromise) { + try { + await luaFactoryPromise; + } catch (error) { + luaFactoryPromise = null; + } + return; + } + + try { + luaFactoryPromise = makeLuaFactory(); + await luaFactoryPromise; + } finally { + luaFactoryPromise = null; + } +} + +export function luaCodeWrapper(code: string) { + return ` +json = require 'json' + +function getChat(id, index) + return json.decode(getChatMain(id, index)) +end + +function getFullChat(id) + return json.decode(getFullChatMain(id)) +end + +function setFullChat(id, value) + setFullChatMain(id, json.encode(value)) +end + +function log(value) + logMain(json.encode(value)) +end + +function getLoreBooks(id, search) + return json.decode(getLoreBooksMain(id, search)) +end + + +function loadLoreBooks(id) + return json.decode(loadLoreBooksMain(id):await()) +end + +function LLM(id, prompt) + return json.decode(LLMMain(id, json.encode(prompt)):await()) +end + +function axLLM(id, prompt) + return json.decode(axLLMMain(id, json.encode(prompt)):await()) +end + +local editRequestFuncs = {} +local editDisplayFuncs = {} +local editInputFuncs = {} +local editOutputFuncs = {} + +function listenEdit(type, func) + if type == 'editRequest' then + editRequestFuncs[#editRequestFuncs + 1] = func + return + end + + if type == 'editDisplay' then + editDisplayFuncs[#editDisplayFuncs + 1] = func + return + end + + if type == 'editInput' then + editInputFuncs[#editInputFuncs + 1] = func + return + end + + if type == 'editOutput' then + editOutputFuncs[#editOutputFuncs + 1] = func + return + end + + throw('Invalid type') +end + +function getState(id, name) + local escapedName = "__"..name + return json.decode(getChatVar(id, escapedName)) +end + +function setState(id, name, value) + local escapedName = "__"..name + setChatVar(id, escapedName, json.encode(value)) +end + +function async(callback) + return function(...) + local co = coroutine.create(callback) + local safe, result = coroutine.resume(co, ...) + + return Promise.create(function(resolve, reject) + local checkresult + local step = function() + if coroutine.status(co) == "dead" then + local send = safe and resolve or reject + return send(result) + end + + safe, result = coroutine.resume(co) + checkresult() + end + + checkresult = function() + if safe and result == Promise.resolve(result) then + result:finally(step) + else + step() + end + end + + checkresult() + end) + end +end + +callListenMain = async(function(type, id, value, meta) + local realValue = json.decode(value) + local realMeta = json.decode(meta) + + if type == 'editRequest' then + for _, func in ipairs(editRequestFuncs) do + realValue = func(id, realValue, realMeta) + end + end + + if type == 'editDisplay' then + for _, func in ipairs(editDisplayFuncs) do + realValue = func(id, realValue, realMeta) + end + end + + if type == 'editInput' then + for _, func in ipairs(editInputFuncs) do + realValue = func(id, realValue, realMeta) + end + end + + if type == 'editOutput' then + for _, func in ipairs(editOutputFuncs) do + realValue = func(id, realValue, realMeta) + end + end + + return json.encode(realValue) +end) + +${code} +` +} + +export async function runLuaCode( + ScriptingEngineState: LuaScriptingEngineState, + mode: string, + accessKey: string, + data: any, + meta: any +) { + let res: any + const luaEngine = ScriptingEngineState.engine + try { + switch (mode) { + case 'input': { + const func = luaEngine.global.get('onInput') + if (func) { + res = await func(accessKey) + } + break + } + case 'output': { + const func = luaEngine.global.get('onOutput') + if (func) { + res = await func(accessKey) + } + break + } + case 'start': { + const func = luaEngine.global.get('onStart') + if (func) { + res = await func(accessKey) + } + break + } + case 'onButtonClick': { + const func = luaEngine.global.get('onButtonClick') + if (func) { + res = await func(accessKey, data) + } + break + } + case 'editRequest': + case 'editDisplay': + case 'editInput': + case 'editOutput': { + const func = luaEngine.global.get('callListenMain') + if (func) { + res = await func(mode, accessKey, JSON.stringify(data), JSON.stringify(meta)) + res = JSON.parse(res) + } + break + } + default: { + const func = luaEngine.global.get(mode) + if (func) { + res = await func(accessKey) + } + break + } + } + if (res === false) { + return { stopSending: true, res } + } + } catch (error) { + console.error(error) + } + return { stopSending: false, res } +} \ No newline at end of file diff --git a/src/ts/process/script/engine/python.ts b/src/ts/process/script/engine/python.ts new file mode 100644 index 0000000000..42f2b09cfb --- /dev/null +++ b/src/ts/process/script/engine/python.ts @@ -0,0 +1,132 @@ +import type { PythonScriptingEngineState } from "."; + +export class PyodideContext { + worker: Worker; + apis: Record any> = {}; + inited: boolean = false; + constructor() { + this.worker = new Worker(new URL('src/ts/process/pyworker.ts', import.meta.url), { + type: 'module' + }) + this.worker.onmessage = (event: MessageEvent) => { + if (event.data.type === 'call') { + const { function: func, args, callId } = event.data; + if (this.apis[func]) { + this.apis[func](...args).then((result) => { + this.worker.postMessage({ + type: 'functionResult', + callId: callId, + result: result + }); + }).catch((error) => { + this.worker.postMessage({ + type: 'error', + error: error.message, + id: callId + }); + }); + } else { + this.worker.postMessage({ + type: 'error', + error: `Function ${func} not found`, + id: callId + }); + } + } + } + } + async declareAPI(name: string, func: (...args: any[]) => any) { + this.apis[name] = func; + } + async init(code: string) { + if (this.inited) { + return; + } + const id = crypto.randomUUID(); + return new Promise((resolve, reject) => { + this.worker.onmessage = (event: MessageEvent) => { + if (event.data.id !== id) { + return + } + + if (event.data.type === 'init') { + this.inited = true; + resolve(); + } else if (event.data.type === 'error') { + reject(new Error(event.data.error)); + } + }; + this.worker.postMessage({ + type: 'init', + code: code, + id: id, + moduleFunctions: Object.keys(this.apis) + }); + }); + } + async python(call: string) { + const id = crypto.randomUUID(); + return new Promise((resolve, reject) => { + this.worker.onmessage = (event: MessageEvent) => { + if (event.data.id !== id) { + return + } + + if (event.data.type === 'python') { + resolve(event.data.call); + } else if (event.data.type === 'error') { + reject(new Error(event.data.error)); + } + }; + this.worker.postMessage({ + type: 'python', + call: call, + id: id + }); + }); + } + async close() { + this.worker.terminate(); + } +} + +export async function runPythonCode( + ScriptingEngineState: PythonScriptingEngineState, + mode: string, + accessKey: string, + data: any, + meta: any +) { + let res: any + switch (mode) { + case 'input': { + res = await ScriptingEngineState.pyodide?.python(`onInput('${accessKey}')`) + break + } + case 'output': { + res = await ScriptingEngineState.pyodide?.python(`onOutput('${accessKey}')`) + break + } + case 'start': { + res = await ScriptingEngineState.pyodide?.python(`onStart('${accessKey}')`) + break + } + case 'onButtonClick': { + res = await ScriptingEngineState.pyodide?.python(`onButtonClick('${accessKey}', '${data}')`) + break + } + case 'editRequest': + case 'editDisplay': + case 'editInput': + case 'editOutput': { + res = await ScriptingEngineState.pyodide?.python(`callListenMain('${mode}', '${accessKey}', '${JSON.stringify(data)}', '${JSON.stringify(meta)}')`) + res = JSON.parse(res) + break + } + default: { + res = await ScriptingEngineState.pyodide?.python(`${mode}('${accessKey}')`) + break + } + } + return { stopSending: false, res } +} diff --git a/src/ts/process/script/scriptings.ts b/src/ts/process/script/scriptings.ts new file mode 100644 index 0000000000..7b10d49a23 --- /dev/null +++ b/src/ts/process/script/scriptings.ts @@ -0,0 +1,106 @@ +import { getChatVar, setChatVar, type simpleCharacterArgument } from "src/ts/parser.svelte"; +import { getCurrentCharacter, getCurrentChat, type Chat, type character, type groupChat } from "src/ts/storage/database.svelte"; +import type { OpenAIChat } from "src/ts/process/index.svelte"; +import { v4 } from "uuid"; +import { declareAPI, getAccessKeys } from "./api"; +import { PyodideContext, runPythonCode } from "./engine/python"; +import { ensureLuaFactory, luaCodeWrapper, luaFactory, runLuaCode } from "./engine/lua"; +import { getOrCreateEngineState, type LuaScriptingEngineState, type PythonScriptingEngineState } from "./engine"; + +export async function runScripted(code: string, arg: { + char?: character | groupChat | simpleCharacterArgument, + chat?: Chat + data?: string | OpenAIChat[], + setVar?: (key: string, value: string) => void, + getVar?: (key: string) => string, + lowLevelAccess?: boolean, + meta?: object, + mode?: string, + type?: 'lua' | 'py' +}) { + const type: 'lua' | 'py' = arg.type ?? 'lua' + const char = arg.char ?? getCurrentCharacter() + const data = arg.data ?? '' + const setVar = arg.setVar ?? setChatVar + const getVar = arg.getVar ?? getChatVar + const meta = arg.meta ?? {} + const mode = arg.mode ?? 'manual' + + let chat = arg.chat ?? getCurrentChat() + let stopSending = false + let lowLevelAccess = arg.lowLevelAccess ?? false + + if (type === 'lua') { + await ensureLuaFactory() + } + let ScriptingEngineState = await getOrCreateEngineState(mode, type); + + return await ScriptingEngineState.mutex.runExclusive(async () => { + ScriptingEngineState.chat = chat + ScriptingEngineState.setVar = setVar + ScriptingEngineState.getVar = getVar + if (code !== ScriptingEngineState.code) { + let declare: (name: string, func: Function) => void + + if (ScriptingEngineState.type === 'lua') { + console.log('Creating new Lua engine for mode:', mode) + ScriptingEngineState.engine?.global.close() + ScriptingEngineState.code = code + ScriptingEngineState.engine = await luaFactory.createEngine({ injectObjects: true }) + const luaEngine = ScriptingEngineState.engine + declare = (name: string, func: Function) => { + luaEngine.global.set(name, func) + } + } + if (ScriptingEngineState.type === 'py') { + console.log('Creating new Pyodide context for mode:', mode) + ScriptingEngineState.pyodide?.close() + ScriptingEngineState.pyodide = new PyodideContext() + declare = (name: string, func: Function) => { + ScriptingEngineState.pyodide?.declareAPI(name, func as any) + } + } + declareAPI(declare, ScriptingEngineState, () => { stopSending = true }, char) + + console.log('Running Lua code:', code) + if (ScriptingEngineState.type === 'lua') { + await ScriptingEngineState.engine?.doString(luaCodeWrapper(code)) + } + if (ScriptingEngineState.type === 'py') { + await ScriptingEngineState.pyodide?.init(code) + } + ScriptingEngineState.code = code + } + let accessKey = v4() + const { ScriptingSafeIds, ScriptingEditDisplayIds, ScriptingLowLevelIds } = getAccessKeys() + if (mode === 'editDisplay') { + ScriptingEditDisplayIds.add(accessKey) + } + else { + ScriptingSafeIds.add(accessKey) + if (lowLevelAccess) { + ScriptingLowLevelIds.add(accessKey) + } + } + + let res: any + if (ScriptingEngineState.type === 'lua') { + const { stopSending: stop, res: result } = await runLuaCode(ScriptingEngineState as LuaScriptingEngineState, mode, accessKey, data, meta) + stopSending = stop + res = result + } + if (ScriptingEngineState.type === 'py') { + const { stopSending: stop, res: result } = await runPythonCode(ScriptingEngineState as PythonScriptingEngineState, mode, accessKey, data, meta) + stopSending = stop + res = result + } + + ScriptingSafeIds.delete(accessKey) + ScriptingLowLevelIds.delete(accessKey) + chat = ScriptingEngineState.chat + + return { + stopSending, chat, res + } + }) +} \ No newline at end of file diff --git a/src/ts/process/script/trigger.ts b/src/ts/process/script/trigger.ts new file mode 100644 index 0000000000..8922a61cd5 --- /dev/null +++ b/src/ts/process/script/trigger.ts @@ -0,0 +1,72 @@ +import type { OpenAIChat } from "src/ts/process/index.svelte"; +import { getModuleTriggers } from "src/ts/process/modules"; +import type { character, groupChat, triggerscript } from "src/ts/storage/database.svelte"; +import { runScripted } from "./scriptings"; +import type { simpleCharacterArgument } from "src/ts/parser.svelte"; + +export async function runLuaEditTrigger(char: character | groupChat | simpleCharacterArgument, mode: string, content: T, meta?: object): Promise { + switch (mode) { + case 'editinput': + mode = 'editInput' + break + case 'editoutput': + mode = 'editOutput' + break + case 'editdisplay': + mode = 'editDisplay' + break + case 'editprocess': + return content + } + + try { + let data = content + + const triggers = char.type === 'group' ? (getModuleTriggers()) : (char.triggerscript.map((v) => { + v.lowLevelAccess = false + return v + }).concat(getModuleTriggers())) + + for (let trigger of triggers) { + if (trigger?.effect?.[0]?.type === 'triggerlua') { + const runResult = await runScripted(trigger.effect[0].code, { + char: char, + lowLevelAccess: false, + mode: mode, + data, + meta, + }) + data = runResult.res ?? data + } + } + + + return data + } catch (error) { + return content + } +} + +export async function runLuaButtonTrigger(char: character | groupChat | simpleCharacterArgument, data: string): Promise { + let runResult + try { + const triggers = char.type === 'group' ? getModuleTriggers() : char.triggerscript.map((v) => ({ + ...v, + lowLevelAccess: char.type !== 'simple' ? char.lowLevelAccess ?? false : false + })).concat(getModuleTriggers()) + + for (let trigger of triggers) { + if (trigger?.effect?.[0]?.type === 'triggerlua') { + runResult = await runScripted(trigger.effect[0].code, { + char: char, + lowLevelAccess: trigger.lowLevelAccess, + mode: 'onButtonClick', + data: data + }) + } + } + } catch (error) { + throw (error) + } + return runResult +} diff --git a/src/ts/process/scriptings.ts b/src/ts/process/scriptings.ts deleted file mode 100644 index cc1a51848d..0000000000 --- a/src/ts/process/scriptings.ts +++ /dev/null @@ -1,1290 +0,0 @@ -import { getChatVar, hasher, setChatVar, getGlobalChatVar, type simpleCharacterArgument, risuChatParser } from "../parser.svelte"; -import { LuaEngine, LuaFactory } from "wasmoon"; -import { getCurrentCharacter, getCurrentChat, getDatabase, setDatabase, type Chat, type character, type groupChat, type triggerscript } from "../storage/database.svelte"; -import { get } from "svelte/store"; -import { ReloadChatPointer, ReloadGUIPointer, selectedCharID } from "../stores.svelte"; -import { alertSelect, alertError, alertInput, alertNormal } from "../alert"; -import { HypaProcesser } from "./memory/hypamemory"; -import { generateAIImage } from "./stableDiff"; -import { writeInlayImage } from "./files/inlays"; -import type { OpenAIChat } from "./index.svelte"; -import { requestChatData } from "./request/request"; -import { v4 } from "uuid"; -import { getModuleLorebooks, getModuleTriggers } from "./modules"; -import { Mutex } from "../mutex"; -import { tokenize } from "../tokenizer"; -import { fetchNative } from "../globalApi.svelte"; -import { loadLoreBookV3Prompt } from './lorebook.svelte'; -import { getPersonaPrompt, getUserName } from '../util'; -let luaFactory:LuaFactory -let ScriptingSafeIds = new Set() -let ScriptingEditDisplayIds = new Set() -let ScriptingLowLevelIds = new Set() -let lastRequestResetTime = 0 -let lastRequestsCount = 0 - -interface BasicScriptingEngineState { - code?: string; - mutex: Mutex; - chat?: Chat; - setVar?: (key:string, value:string) => void, - getVar?: (key:string) => string, -} - -interface LuaScriptingEngineState extends BasicScriptingEngineState { - engine?: LuaEngine; - type: 'lua'; -} - -interface PythonScriptingEngineState extends BasicScriptingEngineState { - pyodide?: PyodideContext - type: 'py'; -} - -type ScriptingEngineState = LuaScriptingEngineState | PythonScriptingEngineState; - -let ScriptingEngines = new Map() -let luaFactoryPromise: Promise | null = null; -let pendingEngineCreations = new Map>(); - -export async function runScripted(code:string, arg:{ - char?:character|groupChat|simpleCharacterArgument, - chat?:Chat - data?: string|OpenAIChat[], - setVar?: (key:string, value:string) => void, - getVar?: (key:string) => string, - lowLevelAccess?: boolean, - meta?: object, - mode?: string, - type?: 'lua'|'py' -}){ - const type: 'lua'|'py' = arg.type ?? 'lua' - const char = arg.char ?? getCurrentCharacter() - const data = arg.data ?? '' - const setVar = arg.setVar ?? setChatVar - const getVar = arg.getVar ?? getChatVar - const meta = arg.meta ?? {} - const mode = arg.mode ?? 'manual' - - let chat = arg.chat ?? getCurrentChat() - let stopSending = false - let lowLevelAccess = arg.lowLevelAccess ?? false - - if(type === 'lua'){ - await ensureLuaFactory() - } - let ScriptingEngineState = await getOrCreateEngineState(mode, type); - - return await ScriptingEngineState.mutex.runExclusive(async () => { - ScriptingEngineState.chat = chat - ScriptingEngineState.setVar = setVar - ScriptingEngineState.getVar = getVar - if (code !== ScriptingEngineState.code) { - let declareAPI:(name: string, func:Function) => void - - if(ScriptingEngineState.type === 'lua'){ - console.log('Creating new Lua engine for mode:', mode) - ScriptingEngineState.engine?.global.close() - ScriptingEngineState.code = code - ScriptingEngineState.engine = await luaFactory.createEngine({injectObjects: true}) - const luaEngine = ScriptingEngineState.engine - declareAPI = (name:string, func:Function) => { - luaEngine.global.set(name, func) - } - } - if(ScriptingEngineState.type === 'py'){ - console.log('Creating new Pyodide context for mode:', mode) - ScriptingEngineState.pyodide?.close() - ScriptingEngineState.pyodide = new PyodideContext() - declareAPI = (name:string, func:Function) => { - ScriptingEngineState.pyodide?.declareAPI(name, func as any) - } - } - declareAPI('getChatVar', (id:string,key:string) => { - return ScriptingEngineState.getVar(key) - }) - declareAPI('setChatVar', (id:string,key:string, value:string) => { - if(!ScriptingSafeIds.has(id) && !ScriptingEditDisplayIds.has(id)){ - return - } - ScriptingEngineState.setVar(key, value) - }) - declareAPI('getGlobalVar', (id:string, key:string) => { - return getGlobalChatVar(key) - }) - declareAPI('stopChat', (id:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - stopSending = true - }) - declareAPI('alertError', (id:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - alertError(value) - }) - declareAPI('alertNormal', (id:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - alertNormal(value) - }) - declareAPI('alertInput', (id:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - return alertInput(value) - }) - declareAPI('alertSelect', (id:string, value:string[]) => { - if(!ScriptingSafeIds.has(id)){ - return - } - return alertSelect(value) - }) - - declareAPI('getChatMain', (id:string, index:number) => { - const chat = ScriptingEngineState.chat.message.at(index) - if(!chat){ - return JSON.stringify(null) - } - const data = { - role: chat.role, - data: chat.data, - time: chat.time ?? 0 - } - return JSON.stringify(data) - }) - - declareAPI('setChat', (id:string, index:number, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const message = ScriptingEngineState.chat.message?.at(index) - if(message){ - message.data = value ?? '' - } - }) - declareAPI('setChatRole', (id:string, index:number, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const message = ScriptingEngineState.chat.message?.at(index) - if(message){ - message.role = value === 'user' ? 'user' : 'char' - } - }) - declareAPI('cutChat', (id:string, start:number, end:number) => { - if(!ScriptingSafeIds.has(id)){ - return - } - ScriptingEngineState.chat.message = ScriptingEngineState.chat.message.slice(start,end) - }) - declareAPI('removeChat', (id:string, index:number) => { - if(!ScriptingSafeIds.has(id)){ - return - } - ScriptingEngineState.chat.message.splice(index, 1) - }) - declareAPI('addChat', (id:string, role:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - let roleData:'user'|'char' = role === 'user' ? 'user' : 'char' - ScriptingEngineState.chat.message.push({role: roleData, data: value ?? ''}) - }) - declareAPI('insertChat', (id:string, index:number, role:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - let roleData:'user'|'char' = role === 'user' ? 'user' : 'char' - ScriptingEngineState.chat.message.splice(index, 0, {role: roleData, data: value ?? ''}) - }) - - declareAPI('getTokens', async (id:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - return await tokenize(value) - }) - - declareAPI('getChatLength', (id:string) => { - return ScriptingEngineState.chat.message.length - }) - - declareAPI('getFullChatMain', (id:string) => { - const data = JSON.stringify(ScriptingEngineState.chat.message.map((v) => { - return { - role: v.role, - data: v.data, - time: v.time ?? 0 - } - })) - return data - }) - - declareAPI('cbs', (value) => { - return risuChatParser(value, { chara: getCurrentCharacter() }) - }) - - declareAPI('setFullChatMain', (id:string, value:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const realValue = JSON.parse(value) - - ScriptingEngineState.chat.message = realValue.map((v) => { - return { - role: v.role, - data: v.data - } - }) - }) - - declareAPI('logMain', (value:string) => { - console.log(JSON.parse(value)) - }) - - declareAPI('reloadDisplay', (id:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) - }) - - declareAPI('reloadChat', (id: string, index: number) => { - if(!ScriptingSafeIds.has(id)){ - return - } - ReloadChatPointer.update((v) => { - v[index] = (v[index] ?? 0) + 1 - return v - }) - }) - - //Low Level Access - declareAPI('similarity', async (id:string, source:string, value:string[]) => { - if(!ScriptingLowLevelIds.has(id)){ - return - } - const processer = new HypaProcesser() - await processer.addText(value) - return await processer.similaritySearch(source) - }) - - declareAPI('request', async (id:string, url:string) => { - if(!ScriptingLowLevelIds.has(id)){ - return - } - - if(lastRequestResetTime + 60000 < Date.now()){ - lastRequestsCount = 0 - lastRequestResetTime = Date.now() - } - - if(lastRequestsCount > 5){ - return JSON.stringify({ - status: 429, - data: 'Too many requests. you can request 5 times per minute' - }) - } - - lastRequestsCount++ - - try { - //for security and other reasons, only get request in 120 char is allowed - if(url.length > 120){ - return JSON.stringify({ - status: 413, - data: 'URL to large. max is 120 characters' - }) - } - - if(!url.startsWith('https://')){ - return JSON.stringify({ - status: 400, - data: "Only https requests are allowed" - }) - } - - const bannedURL = [ - "https://realm.risuai.net", - "https://risuai.net", - "https://risuai.xyz" - ] - - for(const burl of bannedURL){ - - if(url.startsWith(burl)){ - return JSON.stringify({ - status: 400, - data: "request to " + url + ' is not allowed' - }) - } - } - - //browser fetch - const d = await fetchNative(url, { - method: "GET" - }) - const text = await d.text() - return JSON.stringify({ - status: d.status, - data: text - }) - - } catch (error) { - return JSON.stringify({ - status: 400, - data: 'internal error' - }) - } - }) - - declareAPI('generateImage', async (id:string, value:string, negValue:string = '') => { - if(!ScriptingLowLevelIds.has(id)){ - return - } - const gen = await generateAIImage(value, char as character, negValue, 'inlay') - if(!gen){ - return 'Error: Image generation failed' - } - const imgHTML = new Image() - imgHTML.src = gen - const inlay = await writeInlayImage(imgHTML) - return `{{inlay::${inlay}}}` - }) - - declareAPI('hash', async (id:string, value:string) => { - return await hasher(new TextEncoder().encode(value)) - }) - - declareAPI('LLMMain', async (id:string, promptStr:string) => { - let prompt:{ - role: string, - content: string - }[] = JSON.parse(promptStr) - if(!ScriptingLowLevelIds.has(id)){ - return - } - let promptbody:OpenAIChat[] = prompt.map((dict) => { - let role:'system'|'user'|'assistant' = 'assistant' - switch(dict['role']){ - case 'system': - case 'sys': - role = 'system' - break - case 'user': - role = 'user' - break - case 'assistant': - case 'bot': - case 'char':{ - role = 'assistant' - break - } - } - - return { - content: dict['content'] ?? '', - role: role, - } - }) - const result = await requestChatData({ - formated: promptbody, - bias: {}, - useStreaming: false, - noMultiGen: true, - }, 'model') - - if(result.type === 'fail'){ - return JSON.stringify({ - success: false, - result: 'Error: ' + result.result - }) - } - - if(result.type === 'streaming' || result.type === 'multiline'){ - return JSON.stringify({ - success: false, - result: result.result - }) - } - - return JSON.stringify({ - success: true, - result: result.result - }) - }) - - declareAPI('simpleLLM', async (id:string, prompt:string) => { - if(!ScriptingLowLevelIds.has(id)){ - return - } - const result = await requestChatData({ - formated: [{ - role: 'user', - content: prompt - }], - bias: {}, - useStreaming: false, - noMultiGen: true, - }, 'model') - - if(result.type === 'fail'){ - return { - success: false, - result: 'Error: ' + result.result - } - } - - if(result.type === 'streaming' || result.type === 'multiline'){ - return { - success: false, - result: result.result - } - } - - return { - success: true, - result: result.result - } - }) - - declareAPI('getName', async (id:string) => { - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.name - }) - - declareAPI('setName', async (id:string, name:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - if(typeof name !== 'string'){ - throw('Invalid data type') - } - db.characters[selectedChar].name = name - setDatabase(db) - }) - - declareAPI('getDescription', async (id:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - if(char.type === 'group'){ - throw('Character is a group') - } - return char.desc - }) - - declareAPI('setDescription', async (id:string, desc:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char =db.characters[selectedChar] - if(typeof data !== 'string'){ - throw('Invalid data type') - } - if(char.type === 'group'){ - throw('Character is a group') - } - char.desc = desc - db.characters[selectedChar] = char - setDatabase(db) - }) - - declareAPI('getCharacterFirstMessage', async (id:string) => { - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.firstMessage - }) - - declareAPI('setCharacterFirstMessage', async (id:string, data:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - if(typeof data !== 'string'){ - return false - } - char.firstMessage = data - db.characters[selectedChar] = char - setDatabase(db) - return true - }) - - declareAPI('getPersonaName', (id:string) => { - return getUserName() - }) - - declareAPI('getPersonaDescription', (id:string) => { - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - - return risuChatParser(getPersonaPrompt(), { chara: char }) - }) - - declareAPI('getAuthorsNote', (id:string) => { - return ScriptingEngineState.chat?.note ?? '' - }) - - declareAPI('getBackgroundEmbedding', async (id:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.backgroundHTML - }) - - declareAPI('setBackgroundEmbedding', async (id:string, data:string) => { - if(!ScriptingSafeIds.has(id)){ - return - } - const db = getDatabase() - const selectedChar = get(selectedCharID) - if(typeof data !== 'string'){ - return false - } - db.characters[selectedChar].backgroundHTML = data - setDatabase(db) - return true - }) - - // Lore books - declareAPI('getLoreBooksMain', (id:string, search:string) => { - const db = getDatabase() - const selectedChar = db.characters[get(selectedCharID)] - if (selectedChar.type !== 'character') { - return - } - - const loreBooks = [...selectedChar.chats[selectedChar.chatPage]?.localLore ?? [], ...selectedChar.globalLore, ...getModuleLorebooks()] - const found = loreBooks.filter((b) => b.comment === search) - - return JSON.stringify(found.map((b) => ({ ...b, content: risuChatParser(b.content, { chara: selectedChar }) }))) - }) - - type upsertLoreBookOptions = { - alwaysActive?: boolean - insertOrder?: number - key?: string - secondKey?: string - regex?: boolean - } - - declareAPI('upsertLocalLoreBook', (id:string, name:string, content:string, options:upsertLoreBookOptions) => { - if(!ScriptingSafeIds.has(id)){ - return - } - - if (char.type !== 'character') { - return - } - - const { - alwaysActive = false, - insertOrder = 100, - key = '', - regex = false, - secondKey = '', - } = options - - const currentChat = char.chats[char.chatPage] - - const newLocalLoreBooks = currentChat.localLore.filter((book) => book.comment !== name) - newLocalLoreBooks.push({ - alwaysActive, - comment: name, - content: content, - insertorder: insertOrder, - mode: 'normal', - key, - secondkey: secondKey, - selective: !!secondKey, - useRegex: regex, - }) - currentChat.localLore = newLocalLoreBooks - }) - - declareAPI('loadLoreBooksMain', async (id:string, reserve:number) => { - if(!ScriptingLowLevelIds.has(id)){ - return - } - - const db = getDatabase() - - const selectedChar = db.characters[get(selectedCharID)] - - if (selectedChar.type !== 'character') { - return - } - - const fullLoreBooks = (await loadLoreBookV3Prompt()).actives - const maxContext = db.maxContext - reserve - if (maxContext < 0) { - return JSON.stringify([]) - } - - let totalTokens = 0 - const loreBooks = [] - - for (const book of fullLoreBooks) { - const parsed = risuChatParser(book.prompt, { chara: selectedChar }).trim() - if (parsed.length === 0) { - continue - } - - const tokens = await tokenize(parsed) - - if (totalTokens + tokens > maxContext) { - break - } - totalTokens += tokens - loreBooks.push({ - data: parsed, - role: book.role === 'assistant' ? 'char' : book.role, - }) - } - - return JSON.stringify(loreBooks) - }) - - declareAPI('axLLMMain', async (id:string, promptStr:string) => { - let prompt:{ - role: string, - content: string - }[] = JSON.parse(promptStr) - if(!ScriptingLowLevelIds.has(id)){ - return - } - let promptbody:OpenAIChat[] = prompt.map((dict) => { - let role:'system'|'user'|'assistant' = 'assistant' - switch(dict['role']){ - case 'system': - case 'sys': - role = 'system' - break - case 'user': - role = 'user' - break - case 'assistant': - case 'bot': - case 'char':{ - role = 'assistant' - break - } - } - - return { - content: dict['content'] ?? '', - role: role, - } - }) - const result = await requestChatData({ - formated: promptbody, - bias: {}, - useStreaming: false, - noMultiGen: true, - }, 'otherAx') - - if(result.type === 'fail'){ - return JSON.stringify({ - success: false, - result: 'Error: ' + result.result - }) - } - - if(result.type === 'streaming' || result.type === 'multiline'){ - return JSON.stringify({ - success: false, - result: result.result - }) - } - - return JSON.stringify({ - success: true, - result: result.result - }) - }) - - declareAPI('getCharacterLastMessage', (id: string) => { - const chat = ScriptingEngineState.chat - if (!chat) { - return '' - } - - const db = getDatabase() - const selchar = db.characters[get(selectedCharID)] - - let pointer = chat.message.length - 1 - while (pointer >= 0) { - if (chat.message[pointer].role === 'char') { - const messageData = chat.message[pointer].data - return messageData - } - pointer-- - } - - return selchar.firstMessage - }) - - declareAPI('getUserLastMessage', (id: string) => { - const chat = ScriptingEngineState.chat - if (!chat) { - return null - } - - let pointer = chat.message.length - 1 - while (pointer >= 0) { - if (chat.message[pointer].role === 'user') { - const messageData = chat.message[pointer].data - return messageData - } - pointer-- - } - - return null - }) - - declareAPI('getCharacterLastMessage', (id: string) => { - const chat = ScriptingEngineState.chat - if (!chat) { - return '' - } - - const db = getDatabase() - const selchar = db.characters[get(selectedCharID)] - - let pointer = chat.message.length - 1 - while (pointer >= 0) { - if (chat.message[pointer].role === 'char') { - const messageData = chat.message[pointer].data - return messageData - } - pointer-- - } - - return selchar.firstMessage - }) - - declareAPI('getUserLastMessage', (id: string) => { - const chat = ScriptingEngineState.chat - if (!chat) { - return null - } - - let pointer = chat.message.length - 1 - while (pointer >= 0) { - if (chat.message[pointer].role === 'user') { - const messageData = chat.message[pointer].data - return messageData - } - pointer-- - } - - return null - }) - - console.log('Running Lua code:', code) - if(ScriptingEngineState.type === 'lua'){ - await ScriptingEngineState.engine?.doString(luaCodeWrapper(code)) - } - if(ScriptingEngineState.type === 'py'){ - await ScriptingEngineState.pyodide?.init(code) - } - ScriptingEngineState.code = code - } - let accessKey = v4() - if(mode === 'editDisplay'){ - ScriptingEditDisplayIds.add(accessKey) - } - else{ - ScriptingSafeIds.add(accessKey) - if(lowLevelAccess){ - ScriptingLowLevelIds.add(accessKey) - } - } - let res:any - if(ScriptingEngineState.type === 'lua'){ - const luaEngine = ScriptingEngineState.engine - try { - switch(mode){ - case 'input':{ - const func = luaEngine.global.get('onInput') - if(func){ - res = await func(accessKey) - } - break - } - case 'output':{ - const func = luaEngine.global.get('onOutput') - if(func){ - res = await func(accessKey) - } - break - } - case 'start':{ - const func = luaEngine.global.get('onStart') - if(func){ - res = await func(accessKey) - } - break - } - case 'onButtonClick':{ - const func = luaEngine.global.get('onButtonClick') - if(func){ - res = await func(accessKey, data) - } - break - } - case 'editRequest': - case 'editDisplay': - case 'editInput': - case 'editOutput':{ - const func = luaEngine.global.get('callListenMain') - if(func){ - res = await func(mode, accessKey, JSON.stringify(data), JSON.stringify(meta)) - res = JSON.parse(res) - } - break - } - default:{ - const func = luaEngine.global.get(mode) - if(func){ - res = await func(accessKey) - } - break - } - } - if(res === false){ - stopSending = true - } - } catch (error) { - console.error(error) - } - } - if(ScriptingEngineState.type === 'py'){ - switch(mode){ - case 'input':{ - res = await ScriptingEngineState.pyodide?.python(`onInput('${accessKey}')`) - break - } - case 'output':{ - res = await ScriptingEngineState.pyodide?.python(`onOutput('${accessKey}')`) - break - } - case 'start':{ - res = await ScriptingEngineState.pyodide?.python(`onStart('${accessKey}')`) - break - } - case 'onButtonClick':{ - res = await ScriptingEngineState.pyodide?.python(`onButtonClick('${accessKey}', '${data}')`) - break - } - case 'editRequest': - case 'editDisplay': - case 'editInput': - case 'editOutput':{ - res = await ScriptingEngineState.pyodide?.python(`callListenMain('${mode}', '${accessKey}', '${JSON.stringify(data)}', '${JSON.stringify(meta)}')`) - res = JSON.parse(res) - break - } - default:{ - res = await ScriptingEngineState.pyodide?.python(`${mode}('${accessKey}')`) - break - } - } - } - ScriptingSafeIds.delete(accessKey) - ScriptingLowLevelIds.delete(accessKey) - chat = ScriptingEngineState.chat - - return { - stopSending, chat, res - } - }) -} - -async function makeLuaFactory(){ - const _luaFactory = new LuaFactory() - async function mountFile(name:string){ - let code = '' - for(let i = 0; i < 3; i++){ - try { - const res = await fetch('/lua/' + name) - if(res.status >= 200 && res.status < 300){ - code = await res.text() - break - } - } catch (error) {} - } - await _luaFactory.mountFile(name,code) - } - - await mountFile('json.lua') - luaFactory = _luaFactory -} - -async function ensureLuaFactory() { - if (luaFactory) return; - - if (luaFactoryPromise) { - try { - await luaFactoryPromise; - } catch (error) { - luaFactoryPromise = null; - } - return; - } - - try { - luaFactoryPromise = makeLuaFactory(); - await luaFactoryPromise; - } finally { - luaFactoryPromise = null; - } -} - -async function getOrCreateEngineState( - mode: string, - type: 'lua'|'py' -): Promise { - let engineState = ScriptingEngines.get(mode); - if (engineState) { - return engineState; - } - - let pendingCreation = pendingEngineCreations.get(mode); - if (pendingCreation) { - return pendingCreation; - } - - const creationPromise = (async () => { - const engineState: ScriptingEngineState = { - mutex: new Mutex(), - type: type, - }; - ScriptingEngines.set(mode, engineState); - - pendingEngineCreations.delete(mode); - - return engineState; - })(); - - pendingEngineCreations.set(mode, creationPromise); - - return creationPromise; -} - -function luaCodeWrapper(code:string){ - return ` -json = require 'json' - -function getChat(id, index) - return json.decode(getChatMain(id, index)) -end - -function getFullChat(id) - return json.decode(getFullChatMain(id)) -end - -function setFullChat(id, value) - setFullChatMain(id, json.encode(value)) -end - -function log(value) - logMain(json.encode(value)) -end - -function getLoreBooks(id, search) - return json.decode(getLoreBooksMain(id, search)) -end - - -function loadLoreBooks(id) - return json.decode(loadLoreBooksMain(id):await()) -end - -function LLM(id, prompt) - return json.decode(LLMMain(id, json.encode(prompt)):await()) -end - -function axLLM(id, prompt) - return json.decode(axLLMMain(id, json.encode(prompt)):await()) -end - -local editRequestFuncs = {} -local editDisplayFuncs = {} -local editInputFuncs = {} -local editOutputFuncs = {} - -function listenEdit(type, func) - if type == 'editRequest' then - editRequestFuncs[#editRequestFuncs + 1] = func - return - end - - if type == 'editDisplay' then - editDisplayFuncs[#editDisplayFuncs + 1] = func - return - end - - if type == 'editInput' then - editInputFuncs[#editInputFuncs + 1] = func - return - end - - if type == 'editOutput' then - editOutputFuncs[#editOutputFuncs + 1] = func - return - end - - throw('Invalid type') -end - -function getState(id, name) - local escapedName = "__"..name - return json.decode(getChatVar(id, escapedName)) -end - -function setState(id, name, value) - local escapedName = "__"..name - setChatVar(id, escapedName, json.encode(value)) -end - -function async(callback) - return function(...) - local co = coroutine.create(callback) - local safe, result = coroutine.resume(co, ...) - - return Promise.create(function(resolve, reject) - local checkresult - local step = function() - if coroutine.status(co) == "dead" then - local send = safe and resolve or reject - return send(result) - end - - safe, result = coroutine.resume(co) - checkresult() - end - - checkresult = function() - if safe and result == Promise.resolve(result) then - result:finally(step) - else - step() - end - end - - checkresult() - end) - end -end - -callListenMain = async(function(type, id, value, meta) - local realValue = json.decode(value) - local realMeta = json.decode(meta) - - if type == 'editRequest' then - for _, func in ipairs(editRequestFuncs) do - realValue = func(id, realValue, realMeta) - end - end - - if type == 'editDisplay' then - for _, func in ipairs(editDisplayFuncs) do - realValue = func(id, realValue, realMeta) - end - end - - if type == 'editInput' then - for _, func in ipairs(editInputFuncs) do - realValue = func(id, realValue, realMeta) - end - end - - if type == 'editOutput' then - for _, func in ipairs(editOutputFuncs) do - realValue = func(id, realValue, realMeta) - end - end - - return json.encode(realValue) -end) - -${code} -` -} - -export async function runLuaEditTrigger(char:character|groupChat|simpleCharacterArgument, mode:string, content:T, meta?:object):Promise{ - switch(mode){ - case 'editinput': - mode = 'editInput' - break - case 'editoutput': - mode = 'editOutput' - break - case 'editdisplay': - mode = 'editDisplay' - break - case 'editprocess': - return content - } - - try { - let data = content - - const triggers = char.type === 'group' ? (getModuleTriggers()) : (char.triggerscript.map((v) => { - v.lowLevelAccess = false - return v - }).concat(getModuleTriggers())) - - for(let trigger of triggers){ - if(trigger?.effect?.[0]?.type === 'triggerlua'){ - const runResult = await runScripted(trigger.effect[0].code, { - char: char, - lowLevelAccess: false, - mode: mode, - data, - meta, - }) - data = runResult.res ?? data - } - } - - - return data - } catch (error) { - return content - } -} - -export async function runLuaButtonTrigger(char:character|groupChat|simpleCharacterArgument, data:string):Promise{ - let runResult - try { - const triggers = char.type === 'group' ? getModuleTriggers() : char.triggerscript.map((v) => ({ - ...v, - lowLevelAccess: char.type !== 'simple' ? char.lowLevelAccess ?? false : false - })).concat(getModuleTriggers()) - - for(let trigger of triggers){ - if(trigger?.effect?.[0]?.type === 'triggerlua'){ - runResult = await runScripted(trigger.effect[0].code, { - char: char, - lowLevelAccess: trigger.lowLevelAccess, - mode: 'onButtonClick', - data: data - }) - } - } - } catch (error) { - throw(error) - } - return runResult -} - -class PyodideContext{ - worker: Worker; - apis: Record any> = {}; - inited: boolean = false; - constructor(){ - this.worker = new Worker(new URL('./pyworker.ts', import.meta.url), { - type: 'module' - }) - this.worker.onmessage = (event:MessageEvent) => { - if(event.data.type === 'call'){ - const { function: func, args, callId } = event.data; - if(this.apis[func]){ - this.apis[func](...args).then((result) => { - this.worker.postMessage({ - type: 'functionResult', - callId: callId, - result: result - }); - }).catch((error) => { - this.worker.postMessage({ - type: 'error', - error: error.message, - id: callId - }); - }); - } else { - this.worker.postMessage({ - type: 'error', - error: `Function ${func} not found`, - id: callId - }); - } - } - } - } - async declareAPI(name:string, func:(...args:any[]) => any){ - this.apis[name] = func; - } - async init(code:string){ - if(this.inited){ - return; - } - const id = crypto.randomUUID(); - return new Promise((resolve, reject) => { - this.worker.onmessage = (event:MessageEvent) => { - if(event.data.id !== id){ - return - } - - if(event.data.type === 'init'){ - this.inited = true; - resolve(); - } else if(event.data.type === 'error'){ - reject(new Error(event.data.error)); - } - }; - this.worker.postMessage({ - type: 'init', - code: code, - id: id, - moduleFunctions: Object.keys(this.apis) - }); - }); - } - async python(call:string){ - const id = crypto.randomUUID(); - return new Promise((resolve, reject) => { - this.worker.onmessage = (event:MessageEvent) => { - if(event.data.id !== id){ - return - } - - if(event.data.type === 'python'){ - resolve(event.data.call); - } else if(event.data.type === 'error'){ - reject(new Error(event.data.error)); - } - }; - this.worker.postMessage({ - type: 'python', - call: call, - id: id - }); - }); - } - async close(){ - this.worker.terminate(); - } -} \ No newline at end of file diff --git a/src/ts/process/scripts.ts b/src/ts/process/scripts.ts index 6fb12a397a..627f26fc92 100644 --- a/src/ts/process/scripts.ts +++ b/src/ts/process/scripts.ts @@ -8,14 +8,14 @@ import { selectSingleFile } from "../util"; import { assetRegex, type CbsConditions, risuChatParser as risuChatParserOrg, type simpleCharacterArgument } from "../parser.svelte"; import { getModuleAssets, getModuleRegexScripts, getModuleTriggers } from "./modules"; import { HypaProcesser } from "./memory/hypamemory"; -import { runLuaEditTrigger } from "./scriptings"; +import { runLuaEditTrigger } from "./script/trigger"; import { pluginV2 } from "../plugins/plugins"; import { runTrigger } from "./triggers"; const dreg = /{{data}}/g const randomness = /\|\|\|/g -export type ScriptMode = 'editinput'|'editoutput'|'editprocess'|'editdisplay' +export type ScriptMode = 'editinput' | 'editoutput' | 'editprocess' | 'editdisplay' type pScript = { script: customscript, @@ -23,39 +23,39 @@ type pScript = { actions: string[] } -export async function processScript(char:character|groupChat, data:string, mode:ScriptMode, cbsConditions:CbsConditions = {}){ +export async function processScript(char: character | groupChat, data: string, mode: ScriptMode, cbsConditions: CbsConditions = {}) { return (await processScriptFull(char, data, mode, -1, cbsConditions)).data } -export function exportRegex(s?:customscript[]){ +export function exportRegex(s?: customscript[]) { let db = getDatabase() const script = s ?? db.globalscript const data = Buffer.from(JSON.stringify({ type: 'regex', data: script }), 'utf-8') - downloadFile(`regexscript_export.json`,data) + downloadFile(`regexscript_export.json`, data) alertNormal(language.successExport) } -export async function importRegex(o?:customscript[]):Promise{ +export async function importRegex(o?: customscript[]): Promise { o = o ?? [] const filedata = (await selectSingleFile(['json'])).data - if(!filedata){ + if (!filedata) { return o } let db = getDatabase() try { - const imported= JSON.parse(Buffer.from(filedata).toString('utf-8')) - if(imported.type === 'regex' && imported.data){ - const datas:customscript[] = imported.data + const imported = JSON.parse(Buffer.from(filedata).toString('utf-8')) + if (imported.type === 'regex' && imported.data) { + const datas: customscript[] = imported.data const script = o - for(const data of datas){ + for (const data of datas) { script.push(data) } return o } - else{ + else { alertError("File invaid or corrupted") } @@ -71,7 +71,7 @@ let processScriptCache = new Map() function generateScriptCacheKey(scripts: customscript[], data: string, mode: ScriptMode, chatID = -1, cbsConditions: CbsConditions = {}) { let hash = data + '|||' + mode + '|||'; for (const script of scripts) { - if(script.type !== mode){ + if (script.type !== mode) { continue } hash += `${script.flag?.includes('') ? risuChatParser(script.in, { chatID: chatID, cbsConditions }) : script.in}|||${script.out}${chatID}|||${script.flag ?? ''}|||${script.ableFlag ? 1 : 0}`; @@ -79,52 +79,52 @@ function generateScriptCacheKey(scripts: customscript[], data: string, mode: Scr return hash; } -function cacheScript(hash:string, result:string){ +function cacheScript(hash: string, result: string) { processScriptCache.set(hash, result) - if(processScriptCache.size > 1000){ + if (processScriptCache.size > 1000) { processScriptCache.delete(processScriptCache.keys().next().value) } } -function getScriptCache(hash:string){ +function getScriptCache(hash: string) { return processScriptCache.get(hash) } -export function resetScriptCache(){ +export function resetScriptCache() { processScriptCache = new Map() } -export async function processScriptFull(char:character|groupChat|simpleCharacterArgument, data:string, mode:ScriptMode, chatID = -1, cbsConditions:CbsConditions = {}){ +export async function processScriptFull(char: character | groupChat | simpleCharacterArgument, data: string, mode: ScriptMode, chatID = -1, cbsConditions: CbsConditions = {}) { let db = getDatabase() let emoChanged = false - data = await runLuaEditTrigger(char, mode, data, { index:chatID }) + data = await runLuaEditTrigger(char, mode, data, { index: chatID }) - if(mode === 'editdisplay'){ + if (mode === 'editdisplay') { const currentChar = getCurrentCharacter() - if(currentChar.type !== 'group'){ - try{ + if (currentChar.type !== 'group') { + try { const perf = performance.now() const d = await runTrigger(currentChar, 'display', { chat: getCurrentChat(), displayMode: true, displayData: data }) - + data = d?.displayData ?? data console.log('Trigger time', performance.now() - perf) } - catch(e){ + catch (e) { console.error(e) } } } - if(pluginV2[mode].size > 0){ - for(const plugin of pluginV2[mode]){ + if (pluginV2[mode].size > 0) { + for (const plugin of pluginV2[mode]) { const res = await plugin(data) - if(res !== null && res !== undefined){ + if (res !== null && res !== undefined) { data = res } } @@ -134,33 +134,33 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter const scripts = (db.presetRegex ?? []).concat(char.customscript).concat(getModuleRegexScripts()) const hash = generateScriptCacheKey(scripts, data, mode, chatID, cbsConditions) const cached = getScriptCache(hash) - if(cached){ - return {data: cached, emoChanged: false} + if (cached) { + return { data: cached, emoChanged: false } } - - if(scripts.length === 0){ + + if (scripts.length === 0) { cacheScript(hash, data) - return {data, emoChanged} + return { data, emoChanged } } - function executeScript(pscript:pScript){ + function executeScript(pscript: pScript) { const script = pscript.script - - if(script.in === ''){ + + if (script.in === '') { return } - if(script.type === mode){ + if (script.type === mode) { let outScript2 = script.out.replaceAll("$n", "\n") let outScript = outScript2.replace(dreg, "$&") let flag = 'g' - if(script.ableFlag){ + if (script.ableFlag) { flag = script.flag || 'g' } - if(outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom') || pscript.actions.includes('move_top') || pscript.actions.includes('move_bottom')){ + if (outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom') || pscript.actions.includes('move_top') || pscript.actions.includes('move_bottom')) { flag = flag.replace('g', '') //temperary fix } - if(outScript.endsWith('>') && !pscript.actions.includes('no_end_nl')){ + if (outScript.endsWith('>') && !pscript.actions.includes('no_end_nl')) { outScript += '\n' } //remove unsupported flag @@ -168,33 +168,33 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter //remove repeated flags flag = flag.split('').filter((v, i, a) => a.indexOf(v) === i).join('') - - if(flag.length === 0){ + + if (flag.length === 0) { flag = 'u' } let input = script.in - if(pscript.actions.includes('cbs')){ + if (pscript.actions.includes('cbs')) { input = risuChatParser(input, { chatID: chatID, cbsConditions }) } const reg = new RegExp(input, flag) - if(outScript.startsWith('@@') || pscript.actions.length > 0){ - if(reg.test(data)){ - if(outScript.startsWith('@@emo ')){ + if (outScript.startsWith('@@') || pscript.actions.length > 0) { + if (reg.test(data)) { + if (outScript.startsWith('@@emo ')) { const emoName = script.out.substring(6).trim() let charemotions = get(CharEmotion) let tempEmotion = charemotions[char.chaId] - if(!tempEmotion){ + if (!tempEmotion) { tempEmotion = [] } - if(tempEmotion.length > 4){ + if (tempEmotion.length > 4) { tempEmotion.splice(0, 1) } - if(char.type !== 'simple'){ - for(const emo of char.emotionImages){ - if(emo[0] === emoName){ - const emos:[string, string,number] = [emo[0], emo[1], Date.now()] + if (char.type !== 'simple') { + for (const emo of char.emotionImages) { + if (emo[0] === emoName) { + const emos: [string, string, number] = [emo[0], emo[1], Date.now()] tempEmotion.push(emos) charemotions[char.chaId] = tempEmotion CharEmotion.set(charemotions) @@ -204,25 +204,25 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } } } - else if((outScript.startsWith('@@inject') || pscript.actions.includes('inject')) && chatID !== -1){ + else if ((outScript.startsWith('@@inject') || pscript.actions.includes('inject')) && chatID !== -1) { const selchar = db.characters[get(selectedCharID)] selchar.chats[selchar.chatPage].message[chatID].data = data data = data.replace(reg, "") } - else if( + else if ( outScript.startsWith('@@move_top') || outScript.startsWith('@@move_bottom') || pscript.actions.includes('move_top') || pscript.actions.includes('move_bottom') - ){ + ) { const isGlobal = flag.includes('g') const matchAll = isGlobal ? data.matchAll(reg) : [data.match(reg)] data = data.replace(reg, "") - for(const matched of matchAll){ - if(matched){ + for (const matched of matchAll) { + if (matched) { const inData = matched[0] let out = outScript.replace('@@move_top ', '').replace('@@move_bottom ', '') - .replace(/(?{ + .replace(/(? { const index = parseInt(v.substring(1)) - if(index < matched.length){ + if (index < matched.length) { return matched[index] } return v @@ -230,33 +230,33 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter .replace(/\$\&/g, inData) .replace(/(?]+)>/g, (v) => { const groupName = parseInt(v.substring(2, v.length - 1)) - if(matched.groups && matched.groups[groupName]){ + if (matched.groups && matched.groups[groupName]) { return matched.groups[groupName] } return v }) - if(outScript.startsWith('@@move_top') || pscript.actions.includes('move_top')){ - data = out + '\n' +data + if (outScript.startsWith('@@move_top') || pscript.actions.includes('move_top')) { + data = out + '\n' + data } - else{ + else { data = data + '\n' + out } } } } - else{ + else { data = risuChatParser(data.replace(reg, outScript), { chatID: chatID, cbsConditions }) } } - else{ - if((outScript.startsWith('@@repeat_back') || pscript.actions.includes('repeat_back')) && chatID !== -1){ + else { + if ((outScript.startsWith('@@repeat_back') || pscript.actions.includes('repeat_back')) && chatID !== -1) { const v = outScript.split(' ', 2)[1] const selchar = db.characters[get(selectedCharID)] const chat = selchar.chats[selchar.chatPage] let lastChat = chat.fmIndex === -1 ? selchar.firstMessage : selchar.alternateGreetings[chat.fmIndex] let pointer = chatID - 1 - while(pointer >= 0){ - if(chat.message[pointer].role === chat.message[chatID].role){ + while (pointer >= 0) { + if (chat.message[pointer].role === chat.message[chatID].role) { lastChat = chat.message[pointer].data break } @@ -264,11 +264,11 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } const r = lastChat.match(reg) - if(!v){ + if (!v) { data = data + r[0] } - else if(r[0]){ - switch(v){ + else if (r[0]) { + switch (v) { case 'end': data = data + r[0] break @@ -283,32 +283,32 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter break } - } + } } } } - else{ + else { data = risuChatParser(data.replace(reg, outScript), { chatID: chatID, cbsConditions }) } } } - let parsedScripts:pScript[] = [] + let parsedScripts: pScript[] = [] let orderChanged = false - for (const script of scripts){ - if(script.ableFlag && script.flag?.includes('<')){ + for (const script of scripts) { + if (script.ableFlag && script.flag?.includes('<')) { const rregex = /<(.+?)>/g const scriptData = safeStructuredClone(script) let order = 0 - const actions:string[] = [] - scriptData.flag = scriptData.flag?.replace(rregex, (v:string, p1:string) => { + const actions: string[] = [] + scriptData.flag = scriptData.flag?.replace(rregex, (v: string, p1: string) => { const meta = p1.split(',').map((v) => v.trim()) - for(const m of meta){ - if(m.startsWith('order ')){ + for (const m of meta) { + if (m.startsWith('order ')) { order = parseInt(m.substring(6)) orderChanged = true } - else{ + else { actions.push(m) } } @@ -329,30 +329,30 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter }) } - if(orderChanged){ + if (orderChanged) { parsedScripts.sort((a, b) => b.order - a.order) //sort by order } - for (const script of parsedScripts){ + for (const script of parsedScripts) { try { - executeScript(script) + executeScript(script) } catch (error) { console.error(error) } } - - if(db.dynamicAssets && (char.type === 'simple' || char.type === 'character') && char.additionalAssets && char.additionalAssets.length > 0){ - if((!db.dynamicAssetsEditDisplay && mode === 'editdisplay') - || mode === 'editinput' || mode === 'editprocess'){ + + if (db.dynamicAssets && (char.type === 'simple' || char.type === 'character') && char.additionalAssets && char.additionalAssets.length > 0) { + if ((!db.dynamicAssetsEditDisplay && mode === 'editdisplay') + || mode === 'editinput' || mode === 'editprocess') { cacheScript(hash, data) - return {data, emoChanged} + return { data, emoChanged } } const assetNames = char.additionalAssets.map((v) => v[0]) const moduleAssets = getModuleAssets() - if(moduleAssets.length > 0){ - for(const asset of moduleAssets){ + if (moduleAssets.length > 0) { + for (const asset of moduleAssets) { assetNames.push(asset[0]) } } @@ -361,18 +361,18 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter await processer.addText(assetNames) const matches = data.matchAll(assetRegex) - for(const match of matches){ + for (const match of matches) { const type = match[1] const assetName = match[2] const cacheKey = char.chaId + '::' + assetName - if(type !== 'emotion' && type !== 'source'){ - if(bestMatchCache.has(cacheKey)){ + if (type !== 'emotion' && type !== 'source') { + if (bestMatchCache.has(cacheKey)) { data = data.replaceAll(match[0], `{{${type}::${bestMatchCache.get(cacheKey)}}}`) } - else if(!assetNames.includes(assetName)){ + else if (!assetNames.includes(assetName)) { const searched = await processer.similaritySearch(assetName) const bestMatch = searched[0] - if(bestMatch){ + if (bestMatch) { data = data.replaceAll(match[0], `{{${type}::${bestMatch}}}`) bestMatchCache.set(cacheKey, bestMatch) } @@ -383,7 +383,7 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter cacheScript(hash, data) - return {data, emoChanged} + return { data, emoChanged } } diff --git a/src/ts/process/triggers.ts b/src/ts/process/triggers.ts index 2db07b8e5e..5b0a9f6748 100644 --- a/src/ts/process/triggers.ts +++ b/src/ts/process/triggers.ts @@ -12,105 +12,105 @@ import { HypaProcesser } from "./memory/hypamemory"; import { requestChatData, type OpenAIChatExtra } from "./request/request"; import { generateAIImage } from "./stableDiff"; import { writeInlayImage } from "./files/inlays"; -import { runScripted } from "./scriptings"; +import { runScripted } from "./script/scriptings"; import { calcString } from "./infunctions"; -export interface triggerscript{ +export interface triggerscript { comment: string; - type: 'start'|'manual'|'output'|'input'|'display'|'request' + type: 'start' | 'manual' | 'output' | 'input' | 'display' | 'request' conditions: triggerCondition[] - effect:triggerEffect[] + effect: triggerEffect[] lowLevelAccess?: boolean } -export type triggerCondition = triggerConditionsVar|triggerConditionsExists|triggerConditionsChatIndex - -export type triggerEffect = triggerEffectV1|triggerCode|triggerEffectV2 -export type triggerEffectV1 = triggerEffectCutChat|triggerEffectModifyChat|triggerEffectImgGen|triggerEffectRegex|triggerEffectRunLLM|triggerEffectCheckSimilarity|triggerEffectSendAIprompt|triggerEffectShowAlert|triggerEffectSetvar|triggerEffectSystemPrompt|triggerEffectImpersonate|triggerEffectCommand|triggerEffectStop|triggerEffectRunTrigger|triggerEffectRunAxLLM -export type triggerEffectV2 = triggerV2Header|triggerV2IfVar|triggerV2Else|triggerV2EndIndent|triggerV2SetVar|triggerV2Loop|triggerV2BreakLoop| - triggerV2RunTrigger|triggerV2ConsoleLog|triggerV2StopTrigger|triggerV2CutChat|triggerV2ModifyChat|triggerV2SystemPrompt|triggerV2Impersonate| - triggerV2Command|triggerV2SendAIprompt|triggerV2ImgGen|triggerV2CheckSimilarity|triggerV2RunLLM|triggerV2ShowAlert|triggerV2ExtractRegex| - triggerV2GetLastMessage|triggerV2GetMessageAtIndex|triggerV2GetMessageCount|triggerV2GetLastMessage|triggerV2GetMessageAtIndex| - triggerV2GetMessageCount|triggerV2ModifyLorebook|triggerV2GetLorebook|triggerV2GetLorebookCount|triggerV2GetLorebookEntry| - triggerV2SetLorebookActivation|triggerV2GetLorebookIndexViaName|triggerV2LoopNTimes|triggerV2Random|triggerV2GetCharAt| - triggerV2GetCharCount|triggerV2ToLowerCase|triggerV2ToUpperCase|triggerV2SetCharAt|triggerV2SplitString|triggerV2JoinArrayVar|triggerV2GetCharacterDesc| - triggerV2SetCharacterDesc|triggerV2GetPersonaDesc|triggerV2SetPersonaDesc|triggerV2MakeArrayVar|triggerV2GetArrayVarLength|triggerV2GetArrayVar|triggerV2SetArrayVar| - triggerV2PushArrayVar|triggerV2PopArrayVar|triggerV2ShiftArrayVar|triggerV2UnshiftArrayVar|triggerV2SpliceArrayVar|triggerV2GetFirstMessage| - triggerV2SliceArrayVar|triggerV2GetIndexOfValueInArrayVar|triggerV2RemoveIndexFromArrayVar|triggerV2ConcatString|triggerV2GetLastUserMessage| - triggerV2GetLastCharMessage|triggerV2GetAlertInput|triggerV2GetAlertSelect|triggerV2GetDisplayState|triggerV2SetDisplayState|triggerV2UpdateGUI|triggerV2UpdateChatAt|triggerV2Wait| - triggerV2GetRequestState|triggerV2SetRequestState|triggerV2GetRequestStateRole|triggerV2SetRequestStateRole|triggerV2GetRequestStateLength|triggerV2IfAdvanced| - triggerV2QuickSearchChat|triggerV2StopPromptSending|triggerV2Tokenize|triggerV2GetAllLorebooks|triggerV2GetLorebookByName|triggerV2GetLorebookByIndex| - triggerV2CreateLorebook|triggerV2ModifyLorebookByIndex|triggerV2DeleteLorebookByIndex|triggerV2GetLorebookCountNew|triggerV2SetLorebookAlwaysActive| - triggerV2QuickSearchChat|triggerV2StopPromptSending|triggerV2Tokenize|triggerV2RegexTest|triggerV2GetReplaceGlobalNote|triggerV2SetReplaceGlobalNote| - triggerV2GetAuthorNote|triggerV2SetAuthorNote|triggerV2MakeDictVar|triggerV2GetDictVar|triggerV2SetDictVar|triggerV2DeleteDictKey| - triggerV2HasDictKey|triggerV2ClearDict|triggerV2GetDictSize|triggerV2GetDictKeys|triggerV2GetDictValues|triggerV2Calculate|triggerV2ReplaceString|triggerV2Comment| - triggerV2DeclareLocalVar +export type triggerCondition = triggerConditionsVar | triggerConditionsExists | triggerConditionsChatIndex + +export type triggerEffect = triggerEffectV1 | triggerCode | triggerEffectV2 +export type triggerEffectV1 = triggerEffectCutChat | triggerEffectModifyChat | triggerEffectImgGen | triggerEffectRegex | triggerEffectRunLLM | triggerEffectCheckSimilarity | triggerEffectSendAIprompt | triggerEffectShowAlert | triggerEffectSetvar | triggerEffectSystemPrompt | triggerEffectImpersonate | triggerEffectCommand | triggerEffectStop | triggerEffectRunTrigger | triggerEffectRunAxLLM +export type triggerEffectV2 = triggerV2Header | triggerV2IfVar | triggerV2Else | triggerV2EndIndent | triggerV2SetVar | triggerV2Loop | triggerV2BreakLoop | + triggerV2RunTrigger | triggerV2ConsoleLog | triggerV2StopTrigger | triggerV2CutChat | triggerV2ModifyChat | triggerV2SystemPrompt | triggerV2Impersonate | + triggerV2Command | triggerV2SendAIprompt | triggerV2ImgGen | triggerV2CheckSimilarity | triggerV2RunLLM | triggerV2ShowAlert | triggerV2ExtractRegex | + triggerV2GetLastMessage | triggerV2GetMessageAtIndex | triggerV2GetMessageCount | triggerV2GetLastMessage | triggerV2GetMessageAtIndex | + triggerV2GetMessageCount | triggerV2ModifyLorebook | triggerV2GetLorebook | triggerV2GetLorebookCount | triggerV2GetLorebookEntry | + triggerV2SetLorebookActivation | triggerV2GetLorebookIndexViaName | triggerV2LoopNTimes | triggerV2Random | triggerV2GetCharAt | + triggerV2GetCharCount | triggerV2ToLowerCase | triggerV2ToUpperCase | triggerV2SetCharAt | triggerV2SplitString | triggerV2JoinArrayVar | triggerV2GetCharacterDesc | + triggerV2SetCharacterDesc | triggerV2GetPersonaDesc | triggerV2SetPersonaDesc | triggerV2MakeArrayVar | triggerV2GetArrayVarLength | triggerV2GetArrayVar | triggerV2SetArrayVar | + triggerV2PushArrayVar | triggerV2PopArrayVar | triggerV2ShiftArrayVar | triggerV2UnshiftArrayVar | triggerV2SpliceArrayVar | triggerV2GetFirstMessage | + triggerV2SliceArrayVar | triggerV2GetIndexOfValueInArrayVar | triggerV2RemoveIndexFromArrayVar | triggerV2ConcatString | triggerV2GetLastUserMessage | + triggerV2GetLastCharMessage | triggerV2GetAlertInput | triggerV2GetAlertSelect | triggerV2GetDisplayState | triggerV2SetDisplayState | triggerV2UpdateGUI | triggerV2UpdateChatAt | triggerV2Wait | + triggerV2GetRequestState | triggerV2SetRequestState | triggerV2GetRequestStateRole | triggerV2SetRequestStateRole | triggerV2GetRequestStateLength | triggerV2IfAdvanced | + triggerV2QuickSearchChat | triggerV2StopPromptSending | triggerV2Tokenize | triggerV2GetAllLorebooks | triggerV2GetLorebookByName | triggerV2GetLorebookByIndex | + triggerV2CreateLorebook | triggerV2ModifyLorebookByIndex | triggerV2DeleteLorebookByIndex | triggerV2GetLorebookCountNew | triggerV2SetLorebookAlwaysActive | + triggerV2QuickSearchChat | triggerV2StopPromptSending | triggerV2Tokenize | triggerV2RegexTest | triggerV2GetReplaceGlobalNote | triggerV2SetReplaceGlobalNote | + triggerV2GetAuthorNote | triggerV2SetAuthorNote | triggerV2MakeDictVar | triggerV2GetDictVar | triggerV2SetDictVar | triggerV2DeleteDictKey | + triggerV2HasDictKey | triggerV2ClearDict | triggerV2GetDictSize | triggerV2GetDictKeys | triggerV2GetDictValues | triggerV2Calculate | triggerV2ReplaceString | triggerV2Comment | + triggerV2DeclareLocalVar export type triggerConditionsVar = { - type:'var'|'value' - var:string - value:string - operator:'='|'!='|'>'|'<'|'>='|'<='|'null'|'true' + type: 'var' | 'value' + var: string + value: string + operator: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'null' | 'true' } export type triggerCode = { - type: 'triggercode'|'triggerlua', + type: 'triggercode' | 'triggerlua', code: string } export type triggerConditionsChatIndex = { - type:'chatindex' - value:string - operator:'='|'!='|'>'|'<'|'>='|'<='|'null'|'true' + type: 'chatindex' + value: string + operator: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'null' | 'true' } -export type triggerConditionsExists ={ +export type triggerConditionsExists = { type: 'exists' - value:string - type2: 'strict'|'loose'|'regex', + value: string + type2: 'strict' | 'loose' | 'regex', depth: number } -export interface triggerEffectSetvar{ +export interface triggerEffectSetvar { type: 'setvar', - operator: '='|'+='|'-='|'*='|'/=' - var:string - value:string + operator: '=' | '+=' | '-=' | '*=' | '/=' + var: string + value: string } -export interface triggerEffectCutChat{ +export interface triggerEffectCutChat { type: 'cutchat', start: string, end: string } -export interface triggerEffectModifyChat{ +export interface triggerEffectModifyChat { type: 'modifychat', index: string, value: string } -export interface triggerEffectSystemPrompt{ +export interface triggerEffectSystemPrompt { type: 'systemprompt', - location: 'start'|'historyend'|'promptend', - value:string + location: 'start' | 'historyend' | 'promptend', + value: string } -export interface triggerEffectImpersonate{ +export interface triggerEffectImpersonate { type: 'impersonate' - role: 'user'|'char', - value:string + role: 'user' | 'char', + value: string } -type triggerMode = 'start'|'manual'|'output'|'input'|'display'|'request' +type triggerMode = 'start' | 'manual' | 'output' | 'input' | 'display' | 'request' -export interface triggerEffectCommand{ +export interface triggerEffectCommand { type: 'command', value: string } -export interface triggerEffectRegex{ +export interface triggerEffectRegex { type: 'extractRegex', value: string regex: string @@ -119,27 +119,27 @@ export interface triggerEffectRegex{ inputVar: string } -export interface triggerEffectShowAlert{ +export interface triggerEffectShowAlert { type: 'showAlert', alertType: string value: string inputVar: string } -export interface triggerEffectRunTrigger{ +export interface triggerEffectRunTrigger { type: 'runtrigger', value: string } -export interface triggerEffectStop{ +export interface triggerEffectStop { type: 'stop' } -export interface triggerEffectSendAIprompt{ +export interface triggerEffectSendAIprompt { type: 'sendAIprompt' } -export interface triggerEffectImgGen{ +export interface triggerEffectImgGen { type: 'runImgGen', value: string, negValue: string, @@ -147,27 +147,27 @@ export interface triggerEffectImgGen{ } -export interface triggerEffectCheckSimilarity{ +export interface triggerEffectCheckSimilarity { type: 'checkSimilarity', source: string, value: string, inputVar: string } -export interface triggerEffectRunLLM{ +export interface triggerEffectRunLLM { type: 'runLLM', value: string, inputVar: string } -export interface triggerEffectRunAxLLM{ +export interface triggerEffectRunAxLLM { type: 'runAxLLM', value: string, inputVar: string } export type additonalSysPrompt = { - start:string, + start: string, historyend: string, promptend: string } @@ -180,8 +180,8 @@ export type triggerV2Header = { export type triggerV2IfVar = { type: 'v2If', - condition: '='|'!='|'>'|'<'|'>='|'<=', - targetType: 'var'|'value', + condition: '=' | '!=' | '>' | '<' | '>=' | '<=', + targetType: 'var' | 'value', target: string, source: string, indent: number @@ -200,9 +200,9 @@ export type triggerV2EndIndent = { export type triggerV2SetVar = { type: 'v2SetVar', - operator: '='|'+='|'-='|'*='|'/='|'%=', + operator: '=' | '+=' | '-=' | '*=' | '/=' | '%=', var: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', value: string, indent: number } @@ -215,7 +215,7 @@ export type triggerV2Loop = { export type triggerV2LoopNTimes = { type: 'v2LoopNTimes', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -232,7 +232,7 @@ export type triggerV2RunTrigger = { export type triggerV2ConsoleLog = { type: 'v2ConsoleLog', - sourceType: 'var'|'value', + sourceType: 'var' | 'value', source: string, indent: number } @@ -245,41 +245,41 @@ export type triggerV2StopTrigger = { export type triggerV2CutChat = { type: 'v2CutChat', start: string, - startType: 'var'|'value', + startType: 'var' | 'value', end: string, - endType: 'var'|'value', + endType: 'var' | 'value', indent: number } export type triggerV2ModifyChat = { type: 'v2ModifyChat', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2SystemPrompt = { type: 'v2SystemPrompt', - location: 'start'|'historyend'|'promptend', + location: 'start' | 'historyend' | 'promptend', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2Impersonate = { type: 'v2Impersonate', - role: 'user'|'char', + role: 'user' | 'char', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2Command = { type: 'v2Command', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -291,9 +291,9 @@ export type triggerV2SendAIprompt = { export type triggerV2ImgGen = { type: 'v2ImgGen', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', negValue: string, - negValueType: 'var'|'value', + negValueType: 'var' | 'value', outputVar: string, indent: number } @@ -301,9 +301,9 @@ export type triggerV2ImgGen = { export type triggerV2CheckSimilarity = { type: 'v2CheckSimilarity', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', outputVar: string, indent: number } @@ -311,8 +311,8 @@ export type triggerV2CheckSimilarity = { export type triggerV2RunLLM = { type: 'v2RunLLM', value: string, - valueType: 'var'|'value', - model: 'model'|'submodel', + valueType: 'var' | 'value', + model: 'model' | 'submodel', outputVar: string, indent: number } @@ -320,20 +320,20 @@ export type triggerV2RunLLM = { export type triggerV2ShowAlert = { type: 'v2ShowAlert', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2ExtractRegex = { type: 'v2ExtractRegex', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', regex: string, - regexType: 'var'|'value', + regexType: 'var' | 'value', flags: string, - flagsType: 'var'|'value', + flagsType: 'var' | 'value', result: string, - resultType: 'var'|'value', + resultType: 'var' | 'value', outputVar: string, indent: number } @@ -347,7 +347,7 @@ export type triggerV2GetLastMessage = { export type triggerV2GetMessageAtIndex = { type: 'v2GetMessageAtIndex', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', outputVar: string, indent: number } @@ -361,16 +361,16 @@ export type triggerV2GetMessageCount = { export type triggerV2ModifyLorebook = { type: 'v2ModifyLorebook', target: string, - targetType: 'var'|'value', + targetType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2GetLorebook = { type: 'v2GetLorebook', target: string, - targetType: 'var'|'value', + targetType: 'var' | 'value', outputVar: string, indent: number } @@ -384,7 +384,7 @@ export type triggerV2GetLorebookCount = { export type triggerV2GetLorebookEntry = { type: 'v2GetLorebookEntry', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', outputVar: string, indent: number } @@ -392,7 +392,7 @@ export type triggerV2GetLorebookEntry = { export type triggerV2SetLorebookActivation = { type: 'v2SetLorebookActivation', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', value: boolean, indent: number } @@ -400,7 +400,7 @@ export type triggerV2SetLorebookActivation = { export type triggerV2GetLorebookIndexViaName = { type: 'v2GetLorebookIndexViaName', name: string, - nameType: 'var'|'value', + nameType: 'var' | 'value', outputVar: string, indent: number } @@ -408,9 +408,9 @@ export type triggerV2GetLorebookIndexViaName = { export type triggerV2Random = { type: 'v2Random', min: string, - minType: 'var'|'value', + minType: 'var' | 'value', max: string, - maxType: 'var'|'value', + maxType: 'var' | 'value', outputVar: string, indent: number } @@ -418,9 +418,9 @@ export type triggerV2Random = { export type triggerV2GetCharAt = { type: 'v2GetCharAt', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', outputVar: string, indent: number } @@ -428,7 +428,7 @@ export type triggerV2GetCharAt = { export type triggerV2GetCharCount = { type: 'v2GetCharCount', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', outputVar: string, indent: number } @@ -436,7 +436,7 @@ export type triggerV2GetCharCount = { export type triggerV2ToLowerCase = { type: 'v2ToLowerCase', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', outputVar: string, indent: number } @@ -444,7 +444,7 @@ export type triggerV2ToLowerCase = { export type triggerV2ToUpperCase = { type: 'v2ToUpperCase', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', outputVar: string, indent: number } @@ -452,11 +452,11 @@ export type triggerV2ToUpperCase = { export type triggerV2SetCharAt = { type: 'v2SetCharAt', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', outputVar: string, indent: number } @@ -464,9 +464,9 @@ export type triggerV2SetCharAt = { export type triggerV2SplitString = { type: 'v2SplitString', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', delimiter: string, - delimiterType: 'var'|'value'|'regex', + delimiterType: 'var' | 'value' | 'regex', outputVar: string, indent: number } @@ -474,9 +474,9 @@ export type triggerV2SplitString = { export type triggerV2JoinArrayVar = { type: 'v2JoinArrayVar', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', delimiter: string, - delimiterType: 'var'|'value', + delimiterType: 'var' | 'value', outputVar: string, indent: number } @@ -490,7 +490,7 @@ export type triggerV2GetCharacterDesc = { export type triggerV2SetCharacterDesc = { type: 'v2SetCharacterDesc', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -503,7 +503,7 @@ export type triggerV2GetPersonaDesc = { export type triggerV2SetPersonaDesc = { type: 'v2SetPersonaDesc', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -524,7 +524,7 @@ export type triggerV2GetArrayVar = { type: 'v2GetArrayVar', var: string, index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', outputVar: string, indent: number } @@ -533,9 +533,9 @@ export type triggerV2SetArrayVar = { type: 'v2SetArrayVar', var: string, index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -543,7 +543,7 @@ export type triggerV2PushArrayVar = { type: 'v2PushArrayVar', var: string, value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -565,7 +565,7 @@ export type triggerV2UnshiftArrayVar = { type: 'v2UnshiftArrayVar', var: string, value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -573,9 +573,9 @@ export type triggerV2SpliceArrayVar = { type: 'v2SpliceArrayVar', var: string, start: string, - startType: 'var'|'value', + startType: 'var' | 'value', item: string, - itemType: 'var'|'value', + itemType: 'var' | 'value', indent: number } @@ -583,9 +583,9 @@ export type triggerV2SliceArrayVar = { type: 'v2SliceArrayVar', var: string, start: string, - startType: 'var'|'value', + startType: 'var' | 'value', end: string, - endType: 'var'|'value', + endType: 'var' | 'value', outputVar: string, indent: number } @@ -594,7 +594,7 @@ export type triggerV2GetIndexOfValueInArrayVar = { type: 'v2GetIndexOfValueInArrayVar', var: string, value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', outputVar: string, indent: number } @@ -603,16 +603,16 @@ export type triggerV2RemoveIndexFromArrayVar = { type: 'v2RemoveIndexFromArrayVar', var: string, index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } export type triggerV2ConcatString = { type: 'v2ConcatString', source1: string, - source1Type: 'var'|'value', + source1Type: 'var' | 'value', source2: string, - source2Type: 'var'|'value', + source2Type: 'var' | 'value', outputVar: string, indent: number } @@ -638,7 +638,7 @@ export type triggerV2GetFirstMessage = { export type triggerV2GetAlertInput = { type: 'v2GetAlertInput', display: string, - displayType: 'var'|'value', + displayType: 'var' | 'value', outputVar: string, indent: number } @@ -652,7 +652,7 @@ export type triggerV2GetDisplayState = { export type triggerV2SetDisplayState = { type: 'v2SetDisplayState', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -660,7 +660,7 @@ export type triggerV2GetRequestState = { type: 'v2GetRequestState', outputVar: string, index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } @@ -668,25 +668,25 @@ export type triggerV2GetRequestStateRole = { type: 'v2GetRequestStateRole', outputVar: string, index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } export type triggerV2SetRequestState = { type: 'v2SetRequestState', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } export type triggerV2SetRequestStateRole = { type: 'v2SetRequestStateRole', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } @@ -710,16 +710,16 @@ export type triggerV2UpdateChatAt = { export type triggerV2Wait = { type: 'v2Wait', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2IfAdvanced = { type: 'v2IfAdvanced', - condition: '='|'!='|'>'|'<'|'>='|'<='|'≒'|'∋'|'∈'|'∌'|'∉'|'≡' - targetType: 'var'|'value', + condition: '=' | '!=' | '>' | '<' | '>=' | '<=' | '≒' | '∋' | '∈' | '∌' | '∉' | '≡' + targetType: 'var' | 'value', target: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', source: string, indent: number } @@ -727,10 +727,10 @@ export type triggerV2IfAdvanced = { export type triggerV2QuickSearchChat = { type: 'v2QuickSearchChat', value: string, - valueType: 'var'|'value', - condition: 'loose'|'strict'|'regex', + valueType: 'var' | 'value', + condition: 'loose' | 'strict' | 'regex', depth: string, - depthType: 'var'|'value', + depthType: 'var' | 'value', outputVar: string, indent: number } @@ -744,8 +744,8 @@ export type triggerV2Tokenize = { type: 'v2Tokenize', indent: number, value: string - valueType: "var"|"value" - outputVar:string + valueType: "var" | "value" + outputVar: string } export type triggerV2GetAllLorebooks = { @@ -756,11 +756,11 @@ export type triggerV2GetAllLorebooks = { export type triggerV2RegexTest = { type: 'v2RegexTest', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', regex: string, - regexType: 'var'|'value', + regexType: 'var' | 'value', flags: string, - flagsType: 'var'|'value', + flagsType: 'var' | 'value', outputVar: string, indent: number } @@ -768,7 +768,7 @@ export type triggerV2RegexTest = { export type triggerV2GetLorebookByName = { type: 'v2GetLorebookByName', name: string, - nameType: 'var'|'value', + nameType: 'var' | 'value', outputVar: string, indent: number } @@ -776,7 +776,7 @@ export type triggerV2GetLorebookByName = { export type triggerV2GetLorebookByIndex = { type: 'v2GetLorebookByIndex', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', outputVar: string, indent: number } @@ -784,35 +784,35 @@ export type triggerV2GetLorebookByIndex = { export type triggerV2CreateLorebook = { type: 'v2CreateLorebook', name: string, - nameType: 'var'|'value', + nameType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', content: string, - contentType: 'var'|'value', + contentType: 'var' | 'value', insertOrder: string, - insertOrderType: 'var'|'value', + insertOrderType: 'var' | 'value', indent: number } export type triggerV2ModifyLorebookByIndex = { type: 'v2ModifyLorebookByIndex', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', name: string, - nameType: 'var'|'value', + nameType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', content: string, - contentType: 'var'|'value', + contentType: 'var' | 'value', insertOrder: string, - insertOrderType: 'var'|'value', + insertOrderType: 'var' | 'value', indent: number } export type triggerV2DeleteLorebookByIndex = { type: 'v2DeleteLorebookByIndex', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', indent: number } @@ -825,7 +825,7 @@ export type triggerV2GetLorebookCountNew = { export type triggerV2SetLorebookAlwaysActive = { type: 'v2SetLorebookAlwaysActive', index: string, - indexType: 'var'|'value', + indexType: 'var' | 'value', value: boolean, indent: number } @@ -833,9 +833,9 @@ export type triggerV2SetLorebookAlwaysActive = { export type triggerV2GetAlertSelect = { type: 'v2GetAlertSelect', display: string, - displayType: 'var'|'value', + displayType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', outputVar: string, indent: number } @@ -849,7 +849,7 @@ export type triggerV2GetReplaceGlobalNote = { export type triggerV2SetReplaceGlobalNote = { type: 'v2SetReplaceGlobalNote', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -862,7 +862,7 @@ export type triggerV2GetAuthorNote = { export type triggerV2SetAuthorNote = { type: 'v2SetAuthorNote', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -875,9 +875,9 @@ export type triggerV2MakeDictVar = { export type triggerV2GetDictVar = { type: 'v2GetDictVar', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', outputVar: string, indent: number } @@ -885,29 +885,29 @@ export type triggerV2GetDictVar = { export type triggerV2SetDictVar = { type: 'v2SetDictVar', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } export type triggerV2DeleteDictKey = { type: 'v2DeleteDictKey', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', indent: number } export type triggerV2HasDictKey = { type: 'v2HasDictKey', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', key: string, - keyType: 'var'|'value', + keyType: 'var' | 'value', outputVar: string, indent: number } @@ -921,7 +921,7 @@ export type triggerV2ClearDict = { export type triggerV2GetDictSize = { type: 'v2GetDictSize', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', outputVar: string, indent: number } @@ -929,7 +929,7 @@ export type triggerV2GetDictSize = { export type triggerV2GetDictKeys = { type: 'v2GetDictKeys', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', outputVar: string, indent: number } @@ -937,7 +937,7 @@ export type triggerV2GetDictKeys = { export type triggerV2GetDictValues = { type: 'v2GetDictValues', var: string, - varType: 'var'|'value', + varType: 'var' | 'value', outputVar: string, indent: number } @@ -945,7 +945,7 @@ export type triggerV2GetDictValues = { export type triggerV2Calculate = { type: 'v2Calculate', expression: string, - expressionType: 'var'|'value', + expressionType: 'var' | 'value', outputVar: string, indent: number } @@ -953,15 +953,15 @@ export type triggerV2Calculate = { export type triggerV2ReplaceString = { type: 'v2ReplaceString', source: string, - sourceType: 'var'|'value', + sourceType: 'var' | 'value', regex: string, - regexType: 'var'|'value', + regexType: 'var' | 'value', result: string, - resultType: 'var'|'value', + resultType: 'var' | 'value', replacement: string, - replacementType: 'var'|'value', + replacementType: 'var' | 'value', flags: string, - flagsType: 'var'|'value', + flagsType: 'var' | 'value', outputVar: string, indent: number } @@ -976,7 +976,7 @@ export type triggerV2DeclareLocalVar = { type: 'v2DeclareLocalVar', var: string, value: string, - valueType: 'var'|'value', + valueType: 'var' | 'value', indent: number } @@ -1033,7 +1033,7 @@ export const requestAllowList = [ ...safeSubset ] -export async function runTrigger(char:character,mode:triggerMode, arg:{ +export async function runTrigger(char: character, mode: triggerMode, arg: { chat: Chat, recursiveCount?: number additonalSysPrompt?: additonalSysPrompt @@ -1042,7 +1042,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ displayMode?: boolean displayData?: string tempVars?: Record -}){ +}) { arg.recursiveCount ??= 0 char = arg.displayMode ? char : safeStructuredClone(char) let varChanged = false @@ -1050,8 +1050,8 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ const CharacterlowLevelAccess = char.lowLevelAccess ?? false let sendAIprompt = false const currentChat = getCurrentChat() - let additonalSysPrompt:additonalSysPrompt = arg.additonalSysPrompt ?? { - start:'', + let additonalSysPrompt: additonalSysPrompt = arg.additonalSysPrompt ?? { + start: '', historyend: '', promptend: '' } @@ -1062,15 +1062,15 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ const db = getDatabase() const defaultVariables = parseKeyValue(char.defaultVariables).concat(parseKeyValue(db.templateDefaultVariables)) let chat = arg.displayMode ? arg.chat : safeStructuredClone(arg.chat ?? char.chats[char.chatPage]) - if((!triggers) || (triggers.length === 0)){ + if ((!triggers) || (triggers.length === 0)) { return null } - let tempVars:Record = arg.tempVars ?? {} - + let tempVars: Record = arg.tempVars ?? {} + let localVarScopes: Record>[] = [{}] let currentIndent = 0 - + function getLocalVar(key: string): string | null { if (!localVarScopes || localVarScopes.length === 0) { @@ -1088,7 +1088,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } return null } - + function setLocalVar(key: string, value: string, indent: number) { if (!localVarScopes || localVarScopes.length === 0) { localVarScopes = [{}] @@ -1103,11 +1103,11 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ const finalValue = (value === null || value === undefined) ? 'null' : value currentScope[indent][key] = finalValue } - + function declareLocalVar(key: string, value: string, indent: number) { setLocalVar(key, value, indent) } - + function clearLocalVarsAtIndent(indent: number) { if (!localVarScopes || localVarScopes.length === 0) { return @@ -1127,21 +1127,21 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ }) } - function getVar(key:string){ + function getVar(key: string) { const localVar = getLocalVar(key) - if(localVar !== null){ + if (localVar !== null) { return localVar } - + const state = chat.scriptstate?.['$' + key] - if(state === undefined || state === null){ + if (state === undefined || state === null) { const findResult = defaultVariables.find((f) => { return f[0] === key }) - if(findResult){ + if (findResult) { return findResult[1] } - if(arg.displayMode){ + if (arg.displayMode) { return tempVars[key] ?? 'null' } return 'null' @@ -1149,8 +1149,8 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ return state.toString() } - function setVar(key:string, value:string){ - if(arg.displayMode){ + function setVar(key: string, value: string) { + if (arg.displayMode) { tempVars[key] = value return } @@ -1164,146 +1164,146 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ currentCharacter.chats[currentCharacter.chatPage].scriptstate = chat.scriptstate db.characters[selectedCharId].chats[currentCharacter.chatPage].scriptstate = chat.scriptstate } - - - for(const trigger of triggers){ - let tempVars:Record = {} - if(trigger.effect[0]?.type === 'triggercode' || trigger.effect[0]?.type === 'triggerlua'){ + + for (const trigger of triggers) { + let tempVars: Record = {} + + if (trigger.effect[0]?.type === 'triggercode' || trigger.effect[0]?.type === 'triggerlua') { // } - else if(arg.manualName){ - if(trigger.comment !== arg.manualName){ + else if (arg.manualName) { + if (trigger.comment !== arg.manualName) { continue } } - else if(mode !== trigger.type){ + else if (mode !== trigger.type) { continue } let pass = true - for(const condition of trigger.conditions){ - if(condition.type === 'var' || condition.type === 'chatindex' || condition.type === 'value'){ - let varValue = (condition.type === 'var') ? (getVar(condition.var) ?? 'null') : - (condition.type === 'chatindex') ? (chat.message.length.toString()) : - (condition.type === 'value') ? condition.var : null - - if(varValue === undefined || varValue === null){ + for (const condition of trigger.conditions) { + if (condition.type === 'var' || condition.type === 'chatindex' || condition.type === 'value') { + let varValue = (condition.type === 'var') ? (getVar(condition.var) ?? 'null') : + (condition.type === 'chatindex') ? (chat.message.length.toString()) : + (condition.type === 'value') ? condition.var : null + + if (varValue === undefined || varValue === null) { pass = false break } - else{ - const conditionValue = risuChatParser(condition.value,{chara:char}) - varValue = risuChatParser(varValue,{chara:char}) - switch(condition.operator){ + else { + const conditionValue = risuChatParser(condition.value, { chara: char }) + varValue = risuChatParser(varValue, { chara: char }) + switch (condition.operator) { case 'true': { - if(varValue !== 'true' && varValue !== '1'){ + if (varValue !== 'true' && varValue !== '1') { pass = false } break } case '=': - if(varValue !== conditionValue){ + if (varValue !== conditionValue) { pass = false } break case '!=': - if(varValue === conditionValue){ + if (varValue === conditionValue) { pass = false } break case '>': - if(Number(varValue) <= Number(conditionValue)){ + if (Number(varValue) <= Number(conditionValue)) { pass = false } break case '<': - if(Number(varValue) >= Number(conditionValue)){ + if (Number(varValue) >= Number(conditionValue)) { pass = false } break case '>=': - if(Number(varValue) < Number(conditionValue)){ + if (Number(varValue) < Number(conditionValue)) { pass = false } break case '<=': - if(Number(varValue) > Number(conditionValue)){ + if (Number(varValue) > Number(conditionValue)) { pass = false } break case 'null': - if(varValue !== 'null'){ + if (varValue !== 'null') { pass = false } break } } } - else if(condition.type === 'exists'){ - const conditionValue = risuChatParser(condition.value,{chara:char}) - const val = risuChatParser(conditionValue,{chara:char}) - let da = chat.message.slice(0-condition.depth).map((v)=>v.data).join(' ') - if(condition.type2 === 'strict'){ + else if (condition.type === 'exists') { + const conditionValue = risuChatParser(condition.value, { chara: char }) + const val = risuChatParser(conditionValue, { chara: char }) + let da = chat.message.slice(0 - condition.depth).map((v) => v.data).join(' ') + if (condition.type2 === 'strict') { pass = da.split(' ').includes(val) } - else if(condition.type2 === 'loose'){ + else if (condition.type2 === 'loose') { pass = da.toLowerCase().includes(val.toLowerCase()) } - else if(condition.type2 === 'regex'){ + else if (condition.type2 === 'regex') { pass = new RegExp(val).test(da) } } - if(!pass){ + if (!pass) { break } } - if(!pass){ + if (!pass) { continue } - for(let index = 0; index < trigger.effect.length; index++){ + for (let index = 0; index < trigger.effect.length; index++) { const effect = trigger.effect[index] - if(mode === 'display' && !displayAllowList.includes(effect.type)){ + if (mode === 'display' && !displayAllowList.includes(effect.type)) { continue } - if(mode === 'request' && !requestAllowList.includes(effect.type)){ + if (mode === 'request' && !requestAllowList.includes(effect.type)) { continue } - - if(effect && 'indent' in effect && typeof effect.indent === 'number' && effect.indent >= 0){ + + if (effect && 'indent' in effect && typeof effect.indent === 'number' && effect.indent >= 0) { currentIndent = effect.indent - } else if(!effect || !('indent' in effect)) { + } else if (!effect || !('indent' in effect)) { currentIndent = 0 } - - switch(effect.type){ - case'setvar': { - const effectValue = risuChatParser(effect.value,{chara:char}) - const varKey = risuChatParser(effect.var,{chara:char}) + + switch (effect.type) { + case 'setvar': { + const effectValue = risuChatParser(effect.value, { chara: char }) + const varKey = risuChatParser(effect.var, { chara: char }) let originalVar = Number(getVar(varKey)) - if(Number.isNaN(originalVar)){ + if (Number.isNaN(originalVar)) { originalVar = 0 } let resultValue = '' - switch(effect.operator){ - case '=':{ + switch (effect.operator) { + case '=': { resultValue = effectValue break } - case '+=':{ + case '+=': { resultValue = (originalVar + Number(effectValue)).toString() break } - case '-=':{ + case '-=': { resultValue = (originalVar - Number(effectValue)).toString() break } - case '*=':{ + case '*=': { resultValue = (originalVar * Number(effectValue)).toString() break } - case '/=':{ + case '/=': { resultValue = (originalVar / Number(effectValue)).toString() break } @@ -1311,42 +1311,42 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setVar(varKey, resultValue) break } - case 'systemprompt':{ - const effectValue = risuChatParser(effect.value,{chara:char}) + case 'systemprompt': { + const effectValue = risuChatParser(effect.value, { chara: char }) additonalSysPrompt[effect.location] += effectValue + "\n\n" break } - case 'impersonate':{ - const effectValue = risuChatParser(effect.value,{chara:char}) - if(effect.role === 'user'){ - chat.message.push({role: 'user', data: effectValue}) + case 'impersonate': { + const effectValue = risuChatParser(effect.value, { chara: char }) + if (effect.role === 'user') { + chat.message.push({ role: 'user', data: effectValue }) } - else if(effect.role === 'char'){ - chat.message.push({role: 'char', data: effectValue}) + else if (effect.role === 'char') { + chat.message.push({ role: 'char', data: effectValue }) } break } - case 'command':{ - const effectValue = risuChatParser(effect.value,{chara:char}) + case 'command': { + const effectValue = risuChatParser(effect.value, { chara: char }) await processMultiCommand(effectValue) break } case 'stop': - case 'v2StopPromptSending':{ + case 'v2StopPromptSending': { stopSending = true break } - case 'runtrigger':{ - if(arg.recursiveCount < 10 || trigger.lowLevelAccess){ + case 'runtrigger': { + if (arg.recursiveCount < 10 || trigger.lowLevelAccess) { arg.recursiveCount++ - const r = await runTrigger(char,'manual',{ + const r = await runTrigger(char, 'manual', { chat, recursiveCount: arg.recursiveCount, additonalSysPrompt, stopSending, manualName: effect.value }) - if(r){ + if (r) { additonalSysPrompt = r.additonalSysPrompt chat = r.chat stopSending = r.stopSending @@ -1354,49 +1354,49 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case 'cutchat':{ - const start = Number(risuChatParser(effect.start,{chara:char})) - const end = Number(risuChatParser(effect.end,{chara:char})) - chat.message = chat.message.slice(start,end) + case 'cutchat': { + const start = Number(risuChatParser(effect.start, { chara: char })) + const end = Number(risuChatParser(effect.end, { chara: char })) + chat.message = chat.message.slice(start, end) break } - case 'modifychat':{ - const index = Number(risuChatParser(effect.index,{chara:char})) - const value = risuChatParser(effect.value,{chara:char}) - if(chat.message[index]){ + case 'modifychat': { + const index = Number(risuChatParser(effect.index, { chara: char })) + const value = risuChatParser(effect.value, { chara: char }) + if (chat.message[index]) { chat.message[index].data = value } break } // low level access only - case 'showAlert':{ - if(!trigger.lowLevelAccess){ + case 'showAlert': { + if (!trigger.lowLevelAccess) { break } - if(arg.displayMode){ + if (arg.displayMode) { return } - const effectValue = risuChatParser(effect.value,{chara:char}) - const inputVar = risuChatParser(effect.inputVar,{chara:char}) + const effectValue = risuChatParser(effect.value, { chara: char }) + const inputVar = risuChatParser(effect.inputVar, { chara: char }) - switch(effect.alertType){ - case 'normal':{ + switch (effect.alertType) { + case 'normal': { alertNormal(effectValue) break } - case 'error':{ + case 'error': { alertError(effectValue) break } - case 'input':{ + case 'input': { const val = await alertInput(effectValue) setVar(inputVar, val) break; } - case 'select':{ + case 'select': { const val = await alertSelect(effectValue.split('§')) setVar(inputVar, val) } @@ -1404,23 +1404,23 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ break } - case 'sendAIprompt':{ - if(!trigger.lowLevelAccess){ + case 'sendAIprompt': { + if (!trigger.lowLevelAccess) { break } sendAIprompt = true break } - case 'runLLM':{ - if(!trigger.lowLevelAccess){ + case 'runLLM': { + if (!trigger.lowLevelAccess) { break } - const effectValue = risuChatParser(effect.value,{chara:char}) + const effectValue = risuChatParser(effect.value, { chara: char }) const varName = effect.inputVar - let promptbody:OpenAIChat[] = parseChatML(effectValue) - if(!promptbody){ - promptbody = [{role:'user', content:effectValue}] + let promptbody: OpenAIChat[] = parseChatML(effectValue) + if (!promptbody) { + promptbody = [{ role: 'user', content: effectValue }] } const result = await requestChatData({ formated: promptbody, @@ -1429,36 +1429,36 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ noMultiGen: true, }, 'model') - if(result.type === 'fail' || result.type === 'streaming' || result.type === 'multiline'){ + if (result.type === 'fail' || result.type === 'streaming' || result.type === 'multiline') { setVar(varName, 'Error: ' + result.result) } - else{ + else { setVar(varName, result.result) } break } - case 'checkSimilarity':{ - if(!trigger.lowLevelAccess){ + case 'checkSimilarity': { + if (!trigger.lowLevelAccess) { break } const processer = new HypaProcesser() - const effectValue = risuChatParser(effect.value,{chara:char}) - const source = risuChatParser(effect.source,{chara:char}) + const effectValue = risuChatParser(effect.value, { chara: char }) + const source = risuChatParser(effect.source, { chara: char }) await processer.addText(effectValue.split('§')) const val = await processer.similaritySearch(source) setVar(effect.inputVar, val.join('§')) break } - case 'extractRegex':{ - if(!trigger.lowLevelAccess){ + case 'extractRegex': { + if (!trigger.lowLevelAccess) { break } - const effectValue = risuChatParser(effect.value,{chara:char}) + const effectValue = risuChatParser(effect.value, { chara: char }) const regex = new RegExp(effect.regex, effect.flags) const regexResult = regex.exec(effectValue) const result = effect.result.replace(/\$[0-9]+/g, (match) => { @@ -1470,15 +1470,15 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ break } - case 'runImgGen':{ - if(!trigger.lowLevelAccess){ + case 'runImgGen': { + if (!trigger.lowLevelAccess) { break } - const effectValue = risuChatParser(effect.value,{chara:char}) - const negValue = risuChatParser(effect.negValue,{chara:char}) + const effectValue = risuChatParser(effect.value, { chara: char }) + const negValue = risuChatParser(effect.negValue, { chara: char }) const gen = await generateAIImage(effectValue, char, negValue, 'inlay') - if(!gen){ + if (!gen) { setVar(effect.inputVar, 'Error: Image generation failed') break } @@ -1490,8 +1490,8 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ break } - case 'triggerlua':{ - const triggerCodeResult = await runScripted(effect.code,{ + case 'triggerlua': { + const triggerCodeResult = await runScripted(effect.code, { lowLevelAccess: trigger.lowLevelAccess, mode: mode === 'manual' ? arg.manualName : mode, setVar: setVar, @@ -1500,7 +1500,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ chat: chat, }) - if(triggerCodeResult.stopSending){ + if (triggerCodeResult.stopSending) { stopSending = true } chat = triggerCodeResult.chat @@ -1508,40 +1508,40 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } //V2 triggers - case 'v2Header':{ + case 'v2Header': { //Header for V2 triggers to identify the start of a new trigger break } - case 'v2SetVar':{ - const effectValue = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const varKey = risuChatParser(effect.var,{chara:char}) + case 'v2SetVar': { + const effectValue = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const varKey = risuChatParser(effect.var, { chara: char }) let originalVar = Number(getVar(varKey)) - if(Number.isNaN(originalVar)){ + if (Number.isNaN(originalVar)) { originalVar = 0 } let resultValue = '' - switch(effect.operator){ - case '=':{ + switch (effect.operator) { + case '=': { resultValue = effectValue break } - case '+=':{ + case '+=': { resultValue = (originalVar + Number(effectValue)).toString() break } - case '-=':{ + case '-=': { resultValue = (originalVar - Number(effectValue)).toString() break } - case '*=':{ + case '*=': { resultValue = (originalVar * Number(effectValue)).toString() break } - case '/=':{ + case '/=': { resultValue = (originalVar / Number(effectValue)).toString() break } - case '%=':{ + case '%=': { resultValue = (originalVar % Number(effectValue)).toString() break } @@ -1549,54 +1549,54 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setVar(varKey, resultValue) break } - case 'v2DeclareLocalVar':{ - const effectValue = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const varKey = risuChatParser(effect.var,{chara:char}) + case 'v2DeclareLocalVar': { + const effectValue = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const varKey = risuChatParser(effect.var, { chara: char }) const finalValue = (effectValue === null || effectValue === undefined) ? 'null' : effectValue declareLocalVar(varKey, finalValue, effect.indent) break } case 'v2If': - case 'v2IfAdvanced':{ - const sourceValue = (effect.type === 'v2If' || effect.sourceType === 'var') ? getVar(risuChatParser(effect.source,{chara:char})) : risuChatParser(effect.source,{chara:char}) - const targetValue = effect.targetType === 'value' ? risuChatParser(effect.target,{chara:char}) : getVar(risuChatParser(effect.target,{chara:char})) + case 'v2IfAdvanced': { + const sourceValue = (effect.type === 'v2If' || effect.sourceType === 'var') ? getVar(risuChatParser(effect.source, { chara: char })) : risuChatParser(effect.source, { chara: char }) + const targetValue = effect.targetType === 'value' ? risuChatParser(effect.target, { chara: char }) : getVar(risuChatParser(effect.target, { chara: char })) let pass = false - switch(effect.condition){ - case '=':{ - if(!isNaN(Number(sourceValue)) && !isNaN(Number(targetValue))){ //to check like 1.0 = 1 + switch (effect.condition) { + case '=': { + if (!isNaN(Number(sourceValue)) && !isNaN(Number(targetValue))) { //to check like 1.0 = 1 pass = Number(sourceValue) === Number(targetValue) } - else{ + else { pass = sourceValue === targetValue } break } - case '!=':{ - if(!isNaN(Number(sourceValue)) && !isNaN(Number(targetValue))){ //to check like 1.0 = 1 + case '!=': { + if (!isNaN(Number(sourceValue)) && !isNaN(Number(targetValue))) { //to check like 1.0 = 1 pass = Number(sourceValue) !== Number(targetValue) } - else{ + else { pass = sourceValue !== targetValue } break } - case '>':{ + case '>': { pass = Number(sourceValue) > Number(targetValue) break } - case '<':{ + case '<': { pass = Number(sourceValue) < Number(targetValue) break } - case '>=':{ + case '>=': { pass = Number(sourceValue) >= Number(targetValue) break } - case '<=':{ + case '<=': { pass = Number(sourceValue) <= Number(targetValue) break } - case '∈':{ + case '∈': { try { pass = JSON.parse(targetValue).includes(sourceValue) } catch (error) { @@ -1604,7 +1604,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case '∋':{ + case '∋': { try { pass = JSON.parse(sourceValue).includes(targetValue) } catch (error) { @@ -1612,7 +1612,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case '∉':{ + case '∉': { try { pass = !JSON.parse(targetValue).includes(sourceValue) } catch (error) { @@ -1620,7 +1620,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case '∌':{ + case '∌': { try { pass = !JSON.parse(sourceValue).includes(targetValue) } catch (error) { @@ -1628,39 +1628,39 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case '≒':{ + case '≒': { const num1 = Number(sourceValue) const num2 = Number(targetValue) - if(Number.isNaN(num1) || Number.isNaN(num2)){ - pass = sourceValue.toLocaleLowerCase().replace(/ /g,'') === targetValue.toLocaleLowerCase().replace(/ /g,'') + if (Number.isNaN(num1) || Number.isNaN(num2)) { + pass = sourceValue.toLocaleLowerCase().replace(/ /g, '') === targetValue.toLocaleLowerCase().replace(/ /g, '') } - else{ + else { pass = Math.abs(num1 - num2) < 0.0001 } break } - case '≡':{ - if(targetValue === 'true'){ + case '≡': { + if (targetValue === 'true') { pass = sourceValue === 'true' || sourceValue === '1' } - else if(targetValue === 'false'){ + else if (targetValue === 'false') { pass = !(sourceValue === 'true' || sourceValue === '1') } - else{ + else { pass = sourceValue === targetValue } } } - if(!pass){ + if (!pass) { let indent = effect.indent + 1 - for(; index < trigger.effect.length; index++){ + for (; index < trigger.effect.length; index++) { const ef = trigger.effect[index] as triggerEffectV2 - if(ef.type === 'v2EndIndent' && indent === ef.indent){ + if (ef.type === 'v2EndIndent' && indent === ef.indent) { const nextEf = trigger.effect[index + 1] as triggerEffectV2 indent-- - if(nextEf?.type === 'v2Else' && nextEf?.indent === indent){ + if (nextEf?.type === 'v2Else' && nextEf?.indent === indent) { index++ } @@ -1670,36 +1670,36 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case 'v2Else':{ + case 'v2Else': { //since if handles the else if the if is false, we can skip the else const indent = effect.indent + 1 - for(; index < trigger.effect.length; index++){ + for (; index < trigger.effect.length; index++) { const ef = trigger.effect[index] as triggerEffectV2 - if(ef.type === 'v2EndIndent' && indent === ef.indent){ + if (ef.type === 'v2EndIndent' && indent === ef.indent) { break } } break } - case 'v2EndIndent':{ - if(effect.endOfLoop){ + case 'v2EndIndent': { + if (effect.endOfLoop) { const indent = effect.indent - 1 const originalIndex = index - for(; index >= 0; index--){ + for (; index >= 0; index--) { const ef = trigger.effect[index] as triggerEffectV2 - if((ef.type === 'v2Loop' || ef.type === 'v2LoopNTimes') && indent === ef.indent){ - - if(ef.type === 'v2LoopNTimes'){ - let value = ef.valueType === 'value' ? risuChatParser(ef.value,{chara:char}) : getVar(risuChatParser(ef.value,{chara:char})) + if ((ef.type === 'v2Loop' || ef.type === 'v2LoopNTimes') && indent === ef.indent) { + + if (ef.type === 'v2LoopNTimes') { + let value = ef.valueType === 'value' ? risuChatParser(ef.value, { chara: char }) : getVar(risuChatParser(ef.value, { chara: char })) let valueNum = Number(value) - if(Number.isNaN(valueNum)){ + if (Number.isNaN(valueNum)) { valueNum = 0 } tempVars[index + 'LoopNTimes'] = (tempVars[index + 'LoopNTimes'] ?? 0) + 1 - if(tempVars[index + 'LoopNTimes'] >= valueNum){ + if (tempVars[index + 'LoopNTimes'] >= valueNum) { index = originalIndex } - else{ + else { break } } @@ -1707,44 +1707,44 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ break } } - + //this is for preventing lagging tempVars['loopTimes'] = (tempVars['loopTimes'] ?? 0) + 1 - if(tempVars['loopTimes'] > 100){ + if (tempVars['loopTimes'] > 100) { await sleep(1) tempVars['loopTimes'] = 0 } } - + clearLocalVarsAtIndent(effect.indent) - + break } case 'v2Loop': - case 'v2LoopNTimes':{ + case 'v2LoopNTimes': { //Looping is handled by the v2EndIndent break } - case 'v2BreakLoop':{ - for(; index < trigger.effect.length; index++){ + case 'v2BreakLoop': { + for (; index < trigger.effect.length; index++) { const ef = trigger.effect[index] as triggerEffectV2 - if(ef.type === 'v2EndIndent' && ef.endOfLoop){ + if (ef.type === 'v2EndIndent' && ef.endOfLoop) { break } } break } - case 'v2RunTrigger':{ - if(arg.recursiveCount < 10 || trigger.lowLevelAccess){ + case 'v2RunTrigger': { + if (arg.recursiveCount < 10 || trigger.lowLevelAccess) { arg.recursiveCount++ - const r = await runTrigger(char,'manual',{ + const r = await runTrigger(char, 'manual', { chat, recursiveCount: arg.recursiveCount, additonalSysPrompt, stopSending, manualName: effect.target }) - if(r){ + if (r) { additonalSysPrompt = r.additonalSysPrompt chat = r.chat stopSending = r.stopSending @@ -1752,102 +1752,102 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case 'v2ConsoleLog':{ - const sourceValue = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) + case 'v2ConsoleLog': { + const sourceValue = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) console.log(sourceValue) break } - case 'v2StopTrigger':{ + case 'v2StopTrigger': { index = trigger.effect.length break } - case 'v2CutChat':{ - let start = effect.startType === 'value' ? Number(risuChatParser(effect.start,{chara:char})) : Number(getVar(risuChatParser(effect.start,{chara:char}))) - let end = effect.endType === 'value' ? Number(risuChatParser(effect.end,{chara:char})) : Number(getVar(risuChatParser(effect.end,{chara:char}))) - if(isNaN(start)){ + case 'v2CutChat': { + let start = effect.startType === 'value' ? Number(risuChatParser(effect.start, { chara: char })) : Number(getVar(risuChatParser(effect.start, { chara: char }))) + let end = effect.endType === 'value' ? Number(risuChatParser(effect.end, { chara: char })) : Number(getVar(risuChatParser(effect.end, { chara: char }))) + if (isNaN(start)) { start = 0 } - if(isNaN(end)){ + if (isNaN(end)) { end = chat.message.length } - - chat.message = chat.message.slice(start,end) + + chat.message = chat.message.slice(start, end) break } - case 'v2ModifyChat':{ - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - if(chat.message[index]){ + case 'v2ModifyChat': { + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + if (chat.message[index]) { chat.message[index].data = value } break } - case 'v2SystemPrompt':{ - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SystemPrompt': { + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) additonalSysPrompt[effect.location] += value + "\n\n" break } - case 'v2Impersonate':{ - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - if(effect.role === 'user'){ - chat.message.push({role: 'user', data: value}) + case 'v2Impersonate': { + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + if (effect.role === 'user') { + chat.message.push({ role: 'user', data: value }) } - else if(effect.role === 'char'){ - chat.message.push({role: 'char', data: value}) + else if (effect.role === 'char') { + chat.message.push({ role: 'char', data: value }) } break } - case 'v2Command':{ - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2Command': { + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) await processMultiCommand(value) break } - case 'v2SendAIprompt':{ - if(!trigger.lowLevelAccess){ + case 'v2SendAIprompt': { + if (!trigger.lowLevelAccess) { break } sendAIprompt = true break } - case 'v2ImgGen':{ - if(!trigger.lowLevelAccess){ + case 'v2ImgGen': { + if (!trigger.lowLevelAccess) { break } - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - let negValue = effect.negValueType === 'value' ? risuChatParser(effect.negValue,{chara:char}) : getVar(risuChatParser(effect.negValue,{chara:char})) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + let negValue = effect.negValueType === 'value' ? risuChatParser(effect.negValue, { chara: char }) : getVar(risuChatParser(effect.negValue, { chara: char })) let gen = await generateAIImage(value, char, negValue, 'inlay') - if(!gen){ - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + if (!gen) { + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') break } let imgHTML = new Image() imgHTML.src = gen let inlay = await writeInlayImage(imgHTML) let res = `{{inlay::${inlay}}}` - setVar(risuChatParser(effect.outputVar, {chara:char}), res) + setVar(risuChatParser(effect.outputVar, { chara: char }), res) break } - case 'v2CheckSimilarity':{ - if(!trigger.lowLevelAccess){ + case 'v2CheckSimilarity': { + if (!trigger.lowLevelAccess) { break } - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) let processer = new HypaProcesser() await processer.addText(value.split('§')) let val = await processer.similaritySearch(source) - setVar(risuChatParser(effect.outputVar, {chara:char}), val.join('§')) + setVar(risuChatParser(effect.outputVar, { chara: char }), val.join('§')) break } - case 'v2RunLLM':{ - if(!trigger.lowLevelAccess){ + case 'v2RunLLM': { + if (!trigger.lowLevelAccess) { break } - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) let promptbody = parseChatML(value) - if(!promptbody){ - promptbody = [{role:'user', content:value}] + if (!promptbody) { + promptbody = [{ role: 'user', content: value }] } let result = await requestChatData({ formated: promptbody, @@ -1856,27 +1856,27 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ noMultiGen: true, }, effect.model) - if(result.type === 'fail' || result.type === 'streaming' || result.type === 'multiline'){ - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + if (result.type === 'fail' || result.type === 'streaming' || result.type === 'multiline') { + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } - else{ - setVar(risuChatParser(effect.outputVar, {chara:char}), result.result) + else { + setVar(risuChatParser(effect.outputVar, { chara: char }), result.result) } break } - case 'v2ShowAlert':{ - if(arg.displayMode){ + case 'v2ShowAlert': { + if (arg.displayMode) { return } - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) alertNormal(value) break } - case 'v2ExtractRegex':{ - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2ExtractRegex': { + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) let regex = new RegExp(effect.regex, effect.flags) let regexResult = regex.exec(value) - + let result = '' if (regexResult !== null) { result = effect.result.replace(/\$[0-9]+/g, (match) => { @@ -1887,29 +1887,29 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ result = effect.result.replace(/\$[0-9]+/g, '').replace(/\$&/g, '').replace(/\$\$/g, '$') } - setVar(risuChatParser(effect.outputVar, {chara:char}), result) + setVar(risuChatParser(effect.outputVar, { chara: char }), result) break } - case 'v2GetLastMessage':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), chat.message[chat.message.length - 1]?.data ?? 'null') + case 'v2GetLastMessage': { + setVar(risuChatParser(effect.outputVar, { chara: char }), chat.message[chat.message.length - 1]?.data ?? 'null') break } - case 'v2GetMessageAtIndex':{ - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - setVar(risuChatParser(effect.outputVar, {chara:char}), chat.message[index]?.data ?? 'null') + case 'v2GetMessageAtIndex': { + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + setVar(risuChatParser(effect.outputVar, { chara: char }), chat.message[index]?.data ?? 'null') break } - case 'v2GetMessageCount':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), chat.message.length.toString()) + case 'v2GetMessageCount': { + setVar(risuChatParser(effect.outputVar, { chara: char }), chat.message.length.toString()) break } - case 'v2ModifyLorebook':{ + case 'v2ModifyLorebook': { char.globalLore = char.globalLore ?? [] - const target = effect.targetType === 'value' ? risuChatParser(effect.target,{chara:char}) : getVar(risuChatParser(effect.target,{chara:char})) - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + const target = effect.targetType === 'value' ? risuChatParser(effect.target, { chara: char }) : getVar(risuChatParser(effect.target, { chara: char })) + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) const index = char.globalLore.findIndex((v) => v[0] === target) - if(index !== -1){ + if (index !== -1) { char.globalLore[index][1] = value } @@ -1919,30 +1919,30 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(db.characters[selectedCharId]) break } - case 'v2GetLorebook':{ + case 'v2GetLorebook': { char.globalLore = char.globalLore ?? [] - const target = effect.targetType === 'value' ? risuChatParser(effect.target,{chara:char}) : getVar(risuChatParser(effect.target,{chara:char})) + const target = effect.targetType === 'value' ? risuChatParser(effect.target, { chara: char }) : getVar(risuChatParser(effect.target, { chara: char })) const index = char.globalLore.findIndex((v) => v[0] === target) - setVar(risuChatParser(effect.outputVar, {chara:char}), index === -1 ? 'null' : char.globalLore[index][1]) + setVar(risuChatParser(effect.outputVar, { chara: char }), index === -1 ? 'null' : char.globalLore[index][1]) break } - case 'v2GetLorebookCount':{ + case 'v2GetLorebookCount': { char.globalLore = char.globalLore ?? [] - setVar(risuChatParser(effect.outputVar, {chara:char}), char.globalLore.length.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), char.globalLore.length.toString()) break } - case 'v2GetLorebookEntry':{ + case 'v2GetLorebookEntry': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - if(Number.isNaN(index)){ + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + if (Number.isNaN(index)) { index = 0 } - setVar(risuChatParser(effect.outputVar, {chara:char}), char.globalLore[index]?.[1] ?? 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), char.globalLore[index]?.[1] ?? 'null') break } - case 'v2SetLorebookActivation':{ + case 'v2SetLorebookActivation': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) let value = effect.value char.globalLore[index][2] = value @@ -1953,63 +1953,63 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ break } - case 'v2GetLorebookIndexViaName':{ + case 'v2GetLorebookIndexViaName': { char.globalLore = char.globalLore ?? [] - let name = effect.nameType === 'value' ? risuChatParser(effect.name,{chara:char}) : getVar(risuChatParser(effect.name,{chara:char})) + let name = effect.nameType === 'value' ? risuChatParser(effect.name, { chara: char }) : getVar(risuChatParser(effect.name, { chara: char })) let index = char.globalLore.findIndex((v) => v[0] === name) - setVar(risuChatParser(effect.outputVar, {chara:char}), index.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), index.toString()) break } - case 'v2Random':{ - let min = effect.minType === 'value' ? Number(risuChatParser(effect.min,{chara:char})) : Number(getVar(risuChatParser(effect.min,{chara:char}))) - let max = effect.maxType === 'value' ? Number(risuChatParser(effect.max,{chara:char})) : Number(getVar(risuChatParser(effect.max,{chara:char}))) + case 'v2Random': { + let min = effect.minType === 'value' ? Number(risuChatParser(effect.min, { chara: char })) : Number(getVar(risuChatParser(effect.min, { chara: char }))) + let max = effect.maxType === 'value' ? Number(risuChatParser(effect.max, { chara: char })) : Number(getVar(risuChatParser(effect.max, { chara: char }))) let output = Math.floor(Math.random() * (max - min + 1) + min) - setVar(risuChatParser(effect.outputVar, {chara:char}), output.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), output.toString()) break } - case 'v2GetCharAt':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - setVar(risuChatParser(effect.outputVar, {chara:char}), source[index] ?? 'null') + case 'v2GetCharAt': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + setVar(risuChatParser(effect.outputVar, { chara: char }), source[index] ?? 'null') break } - case 'v2GetCharCount':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), source.length.toString()) + case 'v2GetCharCount': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), source.length.toString()) break } - case 'v2ToLowerCase':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), source.toLowerCase()) + case 'v2ToLowerCase': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), source.toLowerCase()) break } - case 'v2ToUpperCase':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), source.toUpperCase()) + case 'v2ToUpperCase': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), source.toUpperCase()) break } - case 'v2SetCharAt':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SetCharAt': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) const source2 = source.split('') source2[index] = value - setVar(risuChatParser(effect.outputVar, {chara:char}), source2.join('')) + setVar(risuChatParser(effect.outputVar, { chara: char }), source2.join('')) break } - case 'v2SplitString':{ - let source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) + case 'v2SplitString': { + let source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) let delimiter: string - + if (effect.delimiterType === 'value') { - delimiter = risuChatParser(effect.delimiter,{chara:char}) + delimiter = risuChatParser(effect.delimiter, { chara: char }) } else if (effect.delimiterType === 'var') { - delimiter = getVar(risuChatParser(effect.delimiter,{chara:char})) + delimiter = getVar(risuChatParser(effect.delimiter, { chara: char })) } else { - delimiter = risuChatParser(effect.delimiter,{chara:char}) + delimiter = risuChatParser(effect.delimiter, { chara: char }) } - + let result: string[] if (effect.delimiterType === 'regex') { try { @@ -2028,27 +2028,27 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } else { result = source.split(delimiter) } - - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(result)) + + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(result)) break } - case 'v2JoinArrayVar':{ + case 'v2JoinArrayVar': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let arr = JSON.parse(varValue) - let delimiter = effect.delimiterType === 'value' ? risuChatParser(effect.delimiter,{chara:char}) : getVar(risuChatParser(effect.delimiter,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr.join(delimiter)) + let delimiter = effect.delimiterType === 'value' ? risuChatParser(effect.delimiter, { chara: char }) : getVar(risuChatParser(effect.delimiter, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), arr.join(delimiter)) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '') + setVar(risuChatParser(effect.outputVar, { chara: char }), '') } break } - case 'v2GetCharacterDesc':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), char.desc) + case 'v2GetCharacterDesc': { + setVar(risuChatParser(effect.outputVar, { chara: char }), char.desc) break } - case 'v2SetCharacterDesc':{ - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SetCharacterDesc': { + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) char.desc = value const selectedCharId = get(selectedCharID) const db = getDatabase(); @@ -2056,27 +2056,27 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2GetPersonaDesc':{ + case 'v2GetPersonaDesc': { const db = getDatabase() - setVar(risuChatParser(effect.outputVar, {chara:char}), db.personas[db.selectedPersona]?.personaPrompt ?? '') + setVar(risuChatParser(effect.outputVar, { chara: char }), db.personas[db.selectedPersona]?.personaPrompt ?? '') break } - case 'v2SetPersonaDesc':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SetPersonaDesc': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) const db = getDatabase() - if(db.personas[db.selectedPersona]){ + if (db.personas[db.selectedPersona]) { db.personas[db.selectedPersona].personaPrompt = value db.personaPrompt = value setDatabase(db) } break } - case 'v2GetReplaceGlobalNote':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), char.replaceGlobalNote ?? '') + case 'v2GetReplaceGlobalNote': { + setVar(risuChatParser(effect.outputVar, { chara: char }), char.replaceGlobalNote ?? '') break } - case 'v2SetReplaceGlobalNote':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SetReplaceGlobalNote': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) char.replaceGlobalNote = value const selectedCharId = get(selectedCharID) const db = getDatabase(); @@ -2084,364 +2084,364 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2MakeArrayVar':{ - const varName = risuChatParser(effect.var, {chara:char}) - if(varName.startsWith('[') && varName.endsWith(']')){ + case 'v2MakeArrayVar': { + const varName = risuChatParser(effect.var, { chara: char }) + if (varName.startsWith('[') && varName.endsWith(']')) { return } setVar(varName, '[]') break } - case 'v2GetArrayVarLength':{ + case 'v2GetArrayVarLength': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr.length.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), arr.length.toString()) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') } break } - case 'v2GetArrayVar':{ + case 'v2GetArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr[index] ?? 'null') + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + setVar(risuChatParser(effect.outputVar, { chara: char }), arr[index] ?? 'null') } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } break } - case 'v2PushArrayVar':{ + case 'v2PushArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) arr.push(value) setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') } break } - case 'v2PopArrayVar':{ + case 'v2PopArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr.pop() ?? 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), arr.pop() ?? 'null') setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } break } - case 'v2ShiftArrayVar':{ + case 'v2ShiftArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr.shift() ?? 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), arr.shift() ?? 'null') setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } break } - case 'v2UnshiftArrayVar':{ + case 'v2UnshiftArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) arr.unshift(value) setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') } break } - case 'v2SpliceArrayVar':{ + case 'v2SpliceArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let start = effect.startType === 'value' ? Number(risuChatParser(effect.start,{chara:char})) : Number(getVar(risuChatParser(effect.start,{chara:char}))) - let value = effect.itemType === 'value' ? risuChatParser(effect.item,{chara:char}) : getVar(risuChatParser(effect.item,{chara:char})) + let start = effect.startType === 'value' ? Number(risuChatParser(effect.start, { chara: char })) : Number(getVar(risuChatParser(effect.start, { chara: char }))) + let value = effect.itemType === 'value' ? risuChatParser(effect.item, { chara: char }) : getVar(risuChatParser(effect.item, { chara: char })) arr.splice(start, 0, value) setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') } break } - case 'v2SliceArrayVar':{ + case 'v2SliceArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let start = effect.startType === 'value' ? Number(risuChatParser(effect.start,{chara:char})) : Number(getVar(risuChatParser(effect.start,{chara:char}))) - let end = effect.endType === 'value' ? Number(risuChatParser(effect.end,{chara:char})) : Number(getVar(risuChatParser(effect.end,{chara:char}))) - - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(arr.slice(start,end))) + let start = effect.startType === 'value' ? Number(risuChatParser(effect.start, { chara: char })) : Number(getVar(risuChatParser(effect.start, { chara: char }))) + let end = effect.endType === 'value' ? Number(risuChatParser(effect.end, { chara: char })) : Number(getVar(risuChatParser(effect.end, { chara: char }))) + + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(arr.slice(start, end))) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '[]') + setVar(risuChatParser(effect.outputVar, { chara: char }), '[]') } break } - case 'v2GetIndexOfValueInArrayVar':{ + case 'v2GetIndexOfValueInArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), arr.indexOf(value).toString()) + let value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), arr.indexOf(value).toString()) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '-1') + setVar(risuChatParser(effect.outputVar, { chara: char }), '-1') } break } - case 'v2RemoveIndexFromArrayVar':{ + case 'v2RemoveIndexFromArrayVar': { try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) arr.splice(index, 1) setVar(varName, JSON.stringify(arr)) } catch (error) { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) setVar(varName, '[]') } break } - case 'v2ConcatString':{ - let source1 = effect.source1Type === 'value' ? risuChatParser(effect.source1,{chara:char}) : getVar(risuChatParser(effect.source1,{chara:char})) - let source2 = effect.source2Type === 'value' ? risuChatParser(effect.source2,{chara:char}) : getVar(risuChatParser(effect.source2,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), source1 + source2) + case 'v2ConcatString': { + let source1 = effect.source1Type === 'value' ? risuChatParser(effect.source1, { chara: char }) : getVar(risuChatParser(effect.source1, { chara: char })) + let source2 = effect.source2Type === 'value' ? risuChatParser(effect.source2, { chara: char }) : getVar(risuChatParser(effect.source2, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), source1 + source2) break } - case 'v2GetLastUserMessage':{ + case 'v2GetLastUserMessage': { let lastUserMessage = chat.message.slice().reverse().find((v) => v.role === 'user') - setVar(risuChatParser(effect.outputVar, {chara:char}), lastUserMessage?.data ?? 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), lastUserMessage?.data ?? 'null') break } - case 'v2GetLastCharMessage':{ + case 'v2GetLastCharMessage': { let lastCharMessage = chat.message.slice().reverse().find((v) => v.role === 'char') - setVar(risuChatParser(effect.outputVar, {chara:char}), lastCharMessage?.data ?? 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), lastCharMessage?.data ?? 'null') break } - case 'v2GetFirstMessage':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), chat.fmIndex === -1 ? char.firstMessage : char.alternateGreetings[chat.fmIndex]) + case 'v2GetFirstMessage': { + setVar(risuChatParser(effect.outputVar, { chara: char }), chat.fmIndex === -1 ? char.firstMessage : char.alternateGreetings[chat.fmIndex]) break } - case 'v2GetAlertInput':{ - if(arg.displayMode){ + case 'v2GetAlertInput': { + if (arg.displayMode) { return } let value = await alertInput( - effect.displayType === 'value' ? risuChatParser(effect.display,{chara:char}) : getVar(risuChatParser(effect.display,{chara:char})) + effect.displayType === 'value' ? risuChatParser(effect.display, { chara: char }) : getVar(risuChatParser(effect.display, { chara: char })) ) - setVar(risuChatParser(effect.outputVar, {chara:char}), value) + setVar(risuChatParser(effect.outputVar, { chara: char }), value) break } - case 'v2GetAlertSelect':{ - if(arg.displayMode){ + case 'v2GetAlertSelect': { + if (arg.displayMode) { return } - const display = effect.displayType === 'value' ? risuChatParser(effect.display,{chara:char}) : getVar(risuChatParser(effect.display,{chara:char})) - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + const display = effect.displayType === 'value' ? risuChatParser(effect.display, { chara: char }) : getVar(risuChatParser(effect.display, { chara: char })) + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) const options = value.split('|') let result = await alertSelect(options, display) - setVar(risuChatParser(effect.outputVar, {chara:char}), result) + setVar(risuChatParser(effect.outputVar, { chara: char }), result) break } - case 'v2SetArrayVar':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - if(Number.isNaN(index)){ + case 'v2SetArrayVar': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + if (Number.isNaN(index)) { break } try { - const varName = risuChatParser(effect.var, {chara:char}) + const varName = risuChatParser(effect.var, { chara: char }) let varValue = getVar(varName) let arr = JSON.parse(varValue) arr[index] = value setVar(varName, JSON.stringify(arr)) } catch (error) { - + } break } - case 'v2GetDisplayState':{ - if(!arg.displayMode){ + case 'v2GetDisplayState': { + if (!arg.displayMode) { return } - - setVar(risuChatParser(effect.outputVar, {chara:char}), arg.displayData ?? 'null') + + setVar(risuChatParser(effect.outputVar, { chara: char }), arg.displayData ?? 'null') break } - case 'v2SetDisplayState':{ - if(!arg.displayMode){ + case 'v2SetDisplayState': { + if (!arg.displayMode) { return } - arg.displayData = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + arg.displayData = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) break } - case 'v2UpdateGUI':{ + case 'v2UpdateGUI': { ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) break } - case 'v2UpdateChatAt':{ + case 'v2UpdateChatAt': { ReloadChatPointer.update((v) => { v[effect.index] = (v[effect.index] ?? 0) + 1 return v }) break } - case 'v2Wait':{ - let value = effect.valueType === 'value' ? Number(risuChatParser(effect.value,{chara:char})) : Number(getVar(risuChatParser(effect.value,{chara:char}))) + case 'v2Wait': { + let value = effect.valueType === 'value' ? Number(risuChatParser(effect.value, { chara: char })) : Number(getVar(risuChatParser(effect.value, { chara: char }))) await sleep(value * 1000) break } - case 'v2GetRequestState':{ - if(!arg.displayMode){ + case 'v2GetRequestState': { + if (!arg.displayMode) { return } const json = JSON.parse(arg.displayData) as OpenAIChat[] - const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) + const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) const content = json?.[index]?.content ?? 'null' - setVar(risuChatParser(effect.outputVar, {chara:char}), content) + setVar(risuChatParser(effect.outputVar, { chara: char }), content) break } - case 'v2SetRequestState':{ - if(!arg.displayMode){ + case 'v2SetRequestState': { + if (!arg.displayMode) { return } const json = JSON.parse(arg.displayData) as OpenAIChat[] - const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) json[index].content = value arg.displayData = JSON.stringify(json) break } - case 'v2GetRequestStateRole':{ - if(!arg.displayMode){ + case 'v2GetRequestStateRole': { + if (!arg.displayMode) { return } const json = JSON.parse(arg.displayData) as OpenAIChat[] - const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) + const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) const content = json?.[index]?.role ?? 'null' - setVar(risuChatParser(effect.outputVar, {chara:char}), content) + setVar(risuChatParser(effect.outputVar, { chara: char }), content) break } - case 'v2SetRequestStateRole':{ - if(!arg.displayMode){ + case 'v2SetRequestStateRole': { + if (!arg.displayMode) { return } const json = JSON.parse(arg.displayData) as OpenAIChat[] - const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - if(value === 'user' || value === 'assistant' || value === 'system'){ + const index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + if (value === 'user' || value === 'assistant' || value === 'system') { json[index].role = value } arg.displayData = JSON.stringify(json) break } - case 'v2GetRequestStateLength':{ - if(!arg.displayMode){ + case 'v2GetRequestStateLength': { + if (!arg.displayMode) { return } const json = JSON.parse(arg.displayData) as OpenAIChat[] - setVar(risuChatParser(effect.outputVar, {chara:char}), json.length.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), json.length.toString()) break } - case 'v2QuickSearchChat':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const depth = effect.depthType === 'value' ? Number(risuChatParser(effect.depth,{chara:char})) : Number(getVar(risuChatParser(effect.depth,{chara:char}))) + case 'v2QuickSearchChat': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const depth = effect.depthType === 'value' ? Number(risuChatParser(effect.depth, { chara: char })) : Number(getVar(risuChatParser(effect.depth, { chara: char }))) const condition = effect.condition - if(isNaN(depth)){ - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + if (isNaN(depth)) { + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') break } let pass = false - let da = chat.message.slice(0-depth).map((v)=>v.data).join(' ') - if(condition === 'strict'){ + let da = chat.message.slice(0 - depth).map((v) => v.data).join(' ') + if (condition === 'strict') { pass = da.split(' ').includes(value) } - else if(condition === 'loose'){ + else if (condition === 'loose') { pass = da.toLowerCase().includes(value.toLowerCase()) } - else if(condition === 'regex'){ + else if (condition === 'regex') { pass = new RegExp(value).test(da) } - setVar(risuChatParser(effect.outputVar, {chara:char}), pass ? '1' : '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), pass ? '1' : '0') break } - case 'v2Tokenize':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), (await tokenize(value)).toString()) + case 'v2Tokenize': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), (await tokenize(value)).toString()) break } - case 'v2GetAllLorebooks':{ + case 'v2GetAllLorebooks': { char.globalLore = char.globalLore ?? [] const allPrompts = char.globalLore .filter(lore => lore && lore.content !== undefined) .map(lore => lore.content) - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(allPrompts)) + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(allPrompts)) break } - case 'v2GetLorebookByName':{ + case 'v2GetLorebookByName': { char.globalLore = char.globalLore ?? [] - const name = effect.nameType === 'value' ? risuChatParser(effect.name,{chara:char}) : getVar(risuChatParser(effect.name,{chara:char})) + const name = effect.nameType === 'value' ? risuChatParser(effect.name, { chara: char }) : getVar(risuChatParser(effect.name, { chara: char })) const regex = new RegExp(name, 'i') const matchingIndices = char.globalLore .map((lore, index) => { - if(lore && lore.comment !== undefined && regex.test(lore.comment)){ + if (lore && lore.comment !== undefined && regex.test(lore.comment)) { return index } return -1 }) .filter(index => index !== -1) - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(matchingIndices)) + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(matchingIndices)) break } - case 'v2GetLorebookByIndex':{ + case 'v2GetLorebookByIndex': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - if(Number.isNaN(index) || index < 0 || index >= char.globalLore.length){ - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + if (Number.isNaN(index) || index < 0 || index >= char.globalLore.length) { + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } else { const loreEntry = char.globalLore[index] - if(loreEntry && loreEntry.content !== undefined){ - setVar(risuChatParser(effect.outputVar, {chara:char}), loreEntry.content) + if (loreEntry && loreEntry.content !== undefined) { + setVar(risuChatParser(effect.outputVar, { chara: char }), loreEntry.content) } else { - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } } break } - case 'v2CreateLorebook':{ + case 'v2CreateLorebook': { char.globalLore = char.globalLore ?? [] - const name = effect.nameType === 'value' ? risuChatParser(effect.name,{chara:char}) : getVar(risuChatParser(effect.name,{chara:char})) - const key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) - const content = effect.contentType === 'value' ? risuChatParser(effect.content,{chara:char}) : getVar(risuChatParser(effect.content,{chara:char})) - const insertOrder = effect.insertOrderType === 'value' ? Number(risuChatParser(effect.insertOrder,{chara:char})) : Number(getVar(risuChatParser(effect.insertOrder,{chara:char}))) - + const name = effect.nameType === 'value' ? risuChatParser(effect.name, { chara: char }) : getVar(risuChatParser(effect.name, { chara: char })) + const key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) + const content = effect.contentType === 'value' ? risuChatParser(effect.content, { chara: char }) : getVar(risuChatParser(effect.content, { chara: char })) + const insertOrder = effect.insertOrderType === 'value' ? Number(risuChatParser(effect.insertOrder, { chara: char })) : Number(getVar(risuChatParser(effect.insertOrder, { chara: char }))) + char.globalLore.push({ key: key, comment: name, @@ -2459,32 +2459,32 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2ModifyLorebookByIndex':{ + case 'v2ModifyLorebookByIndex': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - - if(Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]){ + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + + if (Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]) { break } const currentLore = char.globalLore[index] - - let name = effect.nameType === 'value' ? risuChatParser(effect.name,{chara:char}) : getVar(risuChatParser(effect.name,{chara:char})) + + let name = effect.nameType === 'value' ? risuChatParser(effect.name, { chara: char }) : getVar(risuChatParser(effect.name, { chara: char })) name = name.replace(/{{slot}}/g, currentLore.comment || '') char.globalLore[index].comment = name - - let key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) + + let key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) key = key.replace(/{{slot}}/g, currentLore.key || '') char.globalLore[index].key = key - - let content = effect.contentType === 'value' ? risuChatParser(effect.content,{chara:char}) : getVar(risuChatParser(effect.content,{chara:char})) + + let content = effect.contentType === 'value' ? risuChatParser(effect.content, { chara: char }) : getVar(risuChatParser(effect.content, { chara: char })) content = content.replace(/{{slot}}/g, currentLore.content || '') char.globalLore[index].content = content - - let insertOrder = effect.insertOrderType === 'value' ? risuChatParser(effect.insertOrder,{chara:char}) : getVar(risuChatParser(effect.insertOrder,{chara:char})) + + let insertOrder = effect.insertOrderType === 'value' ? risuChatParser(effect.insertOrder, { chara: char }) : getVar(risuChatParser(effect.insertOrder, { chara: char })) insertOrder = insertOrder.replace(/{{slot}}/g, (currentLore.insertorder || 100).toString()) const insertOrderNum = Number(insertOrder) - if(!Number.isNaN(insertOrderNum)){ + if (!Number.isNaN(insertOrderNum)) { char.globalLore[index].insertorder = insertOrderNum } @@ -2494,11 +2494,11 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2DeleteLorebookByIndex':{ + case 'v2DeleteLorebookByIndex': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - - if(Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]){ + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + + if (Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]) { break } @@ -2510,16 +2510,16 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2GetLorebookCountNew':{ + case 'v2GetLorebookCountNew': { char.globalLore = char.globalLore ?? [] - setVar(risuChatParser(effect.outputVar, {chara:char}), char.globalLore.length.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), char.globalLore.length.toString()) break } - case 'v2SetLorebookAlwaysActive':{ + case 'v2SetLorebookAlwaysActive': { char.globalLore = char.globalLore ?? [] - let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index,{chara:char})) : Number(getVar(risuChatParser(effect.index,{chara:char}))) - - if(Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]){ + let index = effect.indexType === 'value' ? Number(risuChatParser(effect.index, { chara: char })) : Number(getVar(risuChatParser(effect.index, { chara: char }))) + + if (Number.isNaN(index) || index < 0 || index >= char.globalLore.length || !char.globalLore[index]) { break } @@ -2531,28 +2531,28 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setCurrentCharacter(char) break } - case 'v2RegexTest':{ + case 'v2RegexTest': { try { - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const regexPattern = effect.regexType === 'value' ? risuChatParser(effect.regex,{chara:char}) : getVar(risuChatParser(effect.regex,{chara:char})) - const flags = effect.flagsType === 'value' ? risuChatParser(effect.flags,{chara:char}) : getVar(risuChatParser(effect.flags,{chara:char})) + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const regexPattern = effect.regexType === 'value' ? risuChatParser(effect.regex, { chara: char }) : getVar(risuChatParser(effect.regex, { chara: char })) + const flags = effect.flagsType === 'value' ? risuChatParser(effect.flags, { chara: char }) : getVar(risuChatParser(effect.flags, { chara: char })) const regex = new RegExp(regexPattern, flags) const result = regex.test(value) - setVar(risuChatParser(effect.outputVar, {chara:char}), result ? '1' : '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), result ? '1' : '0') } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') } break } - case 'v2GetAuthorNote':{ - setVar(risuChatParser(effect.outputVar, {chara:char}), chat.note ?? '') + case 'v2GetAuthorNote': { + setVar(risuChatParser(effect.outputVar, { chara: char }), chat.note ?? '') break } - case 'v2SetAuthorNote':{ - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) + case 'v2SetAuthorNote': { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) chat.note = value - - if(!arg.displayMode){ + + if (!arg.displayMode) { const selectedCharId = get(selectedCharID) const currentCharacter = getCurrentCharacter() const db = getDatabase() @@ -2562,146 +2562,146 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } break } - case 'v2MakeDictVar':{ - if(effect.var.startsWith('{') && effect.var.endsWith('}')){ + case 'v2MakeDictVar': { + if (effect.var.startsWith('{') && effect.var.endsWith('}')) { return } - setVar(risuChatParser(effect.var, {chara:char}), '{}') + setVar(risuChatParser(effect.var, { chara: char }), '{}') break } - case 'v2GetDictVar':{ + case 'v2GetDictVar': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) - let key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), dict[key] ?? 'null') + let key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), dict[key] ?? 'null') } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), 'null') + setVar(risuChatParser(effect.outputVar, { chara: char }), 'null') } break } - case 'v2SetDictVar':{ + case 'v2SetDictVar': { try { - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) - - if(effect.varType === 'value') { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) + + if (effect.varType === 'value') { break } - - let varValue = getVar(risuChatParser(effect.var,{chara:char})) + + let varValue = getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) dict[key] = value - setVar(risuChatParser(effect.var, {chara:char}), JSON.stringify(dict)) + setVar(risuChatParser(effect.var, { chara: char }), JSON.stringify(dict)) } catch (error) { - if(effect.varType === 'var') { - const value = effect.valueType === 'value' ? risuChatParser(effect.value,{chara:char}) : getVar(risuChatParser(effect.value,{chara:char})) - const key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) + if (effect.varType === 'var') { + const value = effect.valueType === 'value' ? risuChatParser(effect.value, { chara: char }) : getVar(risuChatParser(effect.value, { chara: char })) + const key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) let dict = {} dict[key] = value - setVar(risuChatParser(effect.var, {chara:char}), JSON.stringify(dict)) + setVar(risuChatParser(effect.var, { chara: char }), JSON.stringify(dict)) } } break } - case 'v2DeleteDictKey':{ + case 'v2DeleteDictKey': { try { - if(effect.varType === 'value') { + if (effect.varType === 'value') { break } - - let varValue = getVar(risuChatParser(effect.var,{chara:char})) + + let varValue = getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) - let key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) + let key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) delete dict[key] - setVar(risuChatParser(effect.var, {chara:char}), JSON.stringify(dict)) + setVar(risuChatParser(effect.var, { chara: char }), JSON.stringify(dict)) } catch (error) { - if(effect.varType === 'var') { - setVar(risuChatParser(effect.var, {chara:char}), '{}') + if (effect.varType === 'var') { + setVar(risuChatParser(effect.var, { chara: char }), '{}') } } break } - case 'v2HasDictKey':{ + case 'v2HasDictKey': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) - let key = effect.keyType === 'value' ? risuChatParser(effect.key,{chara:char}) : getVar(risuChatParser(effect.key,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), dict.hasOwnProperty(key) ? '1' : '0') + let key = effect.keyType === 'value' ? risuChatParser(effect.key, { chara: char }) : getVar(risuChatParser(effect.key, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), dict.hasOwnProperty(key) ? '1' : '0') } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') } break } - case 'v2ClearDict':{ - if(effect.var.startsWith('{') && effect.var.endsWith('}')){ + case 'v2ClearDict': { + if (effect.var.startsWith('{') && effect.var.endsWith('}')) { return } - setVar(risuChatParser(effect.var, {chara:char}), '{}') + setVar(risuChatParser(effect.var, { chara: char }), '{}') break } - case 'v2GetDictSize':{ + case 'v2GetDictSize': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) - setVar(risuChatParser(effect.outputVar, {chara:char}), Object.keys(dict).length.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), Object.keys(dict).length.toString()) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') } break } - case 'v2GetDictKeys':{ + case 'v2GetDictKeys': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) let keys = Object.keys(dict) - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(keys)) + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(keys)) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '[]') + setVar(risuChatParser(effect.outputVar, { chara: char }), '[]') } break } - case 'v2GetDictValues':{ + case 'v2GetDictValues': { try { - let varValue = effect.varType === 'value' ? risuChatParser(effect.var,{chara:char}) : getVar(risuChatParser(effect.var,{chara:char})) + let varValue = effect.varType === 'value' ? risuChatParser(effect.var, { chara: char }) : getVar(risuChatParser(effect.var, { chara: char })) let dict = JSON.parse(varValue) let values = Object.values(dict) - setVar(risuChatParser(effect.outputVar, {chara:char}), JSON.stringify(values)) + setVar(risuChatParser(effect.outputVar, { chara: char }), JSON.stringify(values)) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '[]') + setVar(risuChatParser(effect.outputVar, { chara: char }), '[]') } break } - case 'v2Calculate':{ + case 'v2Calculate': { try { - let expression = effect.expressionType === 'value' ? risuChatParser(effect.expression,{chara:char}) : getVar(risuChatParser(effect.expression,{chara:char})) + let expression = effect.expressionType === 'value' ? risuChatParser(effect.expression, { chara: char }) : getVar(risuChatParser(effect.expression, { chara: char })) expression = expression.replace(/\$([a-zA-Z0-9_]+)/g, (_, varName) => { const varValue = getVar(varName) const parsed = parseFloat(varValue) return isNaN(parsed) ? '0' : parsed.toString() }) - + const result = calcString(expression) - setVar(risuChatParser(effect.outputVar, {chara:char}), result.toString()) + setVar(risuChatParser(effect.outputVar, { chara: char }), result.toString()) } catch (error) { - setVar(risuChatParser(effect.outputVar, {chara:char}), '0') + setVar(risuChatParser(effect.outputVar, { chara: char }), '0') } break } - case 'v2ReplaceString':{ + case 'v2ReplaceString': { try { - const source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - const regexPattern = effect.regexType === 'value' ? risuChatParser(effect.regex,{chara:char}) : getVar(risuChatParser(effect.regex,{chara:char})) - const resultFormat = effect.resultType === 'value' ? risuChatParser(effect.result,{chara:char}) : getVar(risuChatParser(effect.result,{chara:char})) - const replacement = effect.replacementType === 'value' ? risuChatParser(effect.replacement,{chara:char}) : getVar(risuChatParser(effect.replacement,{chara:char})) - const flags = effect.flagsType === 'value' ? risuChatParser(effect.flags,{chara:char}) : getVar(risuChatParser(effect.flags,{chara:char})) - + const source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + const regexPattern = effect.regexType === 'value' ? risuChatParser(effect.regex, { chara: char }) : getVar(risuChatParser(effect.regex, { chara: char })) + const resultFormat = effect.resultType === 'value' ? risuChatParser(effect.result, { chara: char }) : getVar(risuChatParser(effect.result, { chara: char })) + const replacement = effect.replacementType === 'value' ? risuChatParser(effect.replacement, { chara: char }) : getVar(risuChatParser(effect.replacement, { chara: char })) + const flags = effect.flagsType === 'value' ? risuChatParser(effect.flags, { chara: char }) : getVar(risuChatParser(effect.flags, { chara: char })) + const regex = new RegExp(regexPattern, flags) const result = source.replace(regex, (...args) => { const match = args[0] const groups = args.slice(1, -2) - + const targetGroupMatch = resultFormat.match(/^\$(\d+)$/) if (targetGroupMatch) { const targetIndex = Number(targetGroupMatch[1]) @@ -2714,42 +2714,42 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ } } } - + return resultFormat.replace(/\$[0-9]+/g, (placeholder) => { const index = Number(placeholder.slice(1)) return index === 0 ? match : (groups[index - 1] || '') }).replace(/\$&/g, match).replace(/\$\$/g, '$') }) - setVar(risuChatParser(effect.outputVar, {chara:char}), result) + setVar(risuChatParser(effect.outputVar, { chara: char }), result) } catch (error) { - const source = effect.sourceType === 'value' ? risuChatParser(effect.source,{chara:char}) : getVar(risuChatParser(effect.source,{chara:char})) - setVar(risuChatParser(effect.outputVar, {chara:char}), source) + const source = effect.sourceType === 'value' ? risuChatParser(effect.source, { chara: char }) : getVar(risuChatParser(effect.source, { chara: char })) + setVar(risuChatParser(effect.outputVar, { chara: char }), source) } break } - case 'v2Comment':{ + case 'v2Comment': { break } } } } - + let caculatedTokens = 0 - if(additonalSysPrompt.start){ + if (additonalSysPrompt.start) { caculatedTokens += await tokenize(additonalSysPrompt.start) } - if(additonalSysPrompt.historyend){ + if (additonalSysPrompt.historyend) { caculatedTokens += await tokenize(additonalSysPrompt.historyend) } - if(additonalSysPrompt.promptend){ + if (additonalSysPrompt.promptend) { caculatedTokens += await tokenize(additonalSysPrompt.promptend) } - if(varChanged){ + if (varChanged) { const currentChat = getCurrentChat() currentChat.scriptstate = chat.scriptstate ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) } - return {additonalSysPrompt, chat, tokens:caculatedTokens, stopSending, sendAIprompt, displayData: arg.displayData, tempVars: arg.tempVars} + return { additonalSysPrompt, chat, tokens: caculatedTokens, stopSending, sendAIprompt, displayData: arg.displayData, tempVars: arg.tempVars } } \ No newline at end of file