diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e36f08a1..0cdf5c07 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -184,7 +184,7 @@ importers:
specifier: ^5.6.0
version: 5.6.0
'@sveltejs/vite-plugin-svelte':
- specifier: ^3.0.1
+ specifier: 3.0.1
version: 3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7))
'@swc/core':
specifier: 1.5.7
@@ -250,13 +250,13 @@ importers:
specifier: ^8.4.33
version: 8.4.33
svelte:
- specifier: ^4.2.8
+ specifier: 4.2.8
version: 4.2.8
svelte-check:
- specifier: ^3.6.3
+ specifier: 3.6.3
version: 3.6.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)
svelte-preprocess:
- specifier: ^5.1.3
+ specifier: 5.1.3
version: 5.1.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)(typescript@5.3.3)
tailwindcss:
specifier: ^3.4.1
@@ -268,13 +268,13 @@ importers:
specifier: ^5.3.3
version: 5.3.3
vite:
- specifier: ^5.2.12
+ specifier: 5.2.12
version: 5.2.12(@types/node@18.19.7)
vite-plugin-top-level-await:
- specifier: ^1.4.1
+ specifier: 1.4.1
version: 1.4.1(rollup@3.29.4)(vite@5.2.12(@types/node@18.19.7))
vite-plugin-wasm:
- specifier: ^3.3.0
+ specifier: 3.3.0
version: 3.3.0(vite@5.2.12(@types/node@18.19.7))
packages:
diff --git a/src/lang/en.ts b/src/lang/en.ts
index 529c31a2..757874d6 100644
--- a/src/lang/en.ts
+++ b/src/lang/en.ts
@@ -664,6 +664,6 @@ export const languageEnglish = {
includePersonaName: "Include Persona Name",
hidePersonaName: "Hide Persona Name",
triggerSwitchWarn: "If you change the trigger type, current triggers will be lost. do you want to continue?",
- codeMode: "Code Mode",
- blockMode: "Block Mode",
+ codeMode: "Code",
+ blockMode: "Block",
}
\ No newline at end of file
diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte
index 5c6703ba..1fa391d2 100644
--- a/src/lib/SideBars/CharConfig.svelte
+++ b/src/lib/SideBars/CharConfig.svelte
@@ -581,19 +581,25 @@
{language.triggerScript}
-
- {#if currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode'}
+ {#if currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode' || currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggerlua'}
{:else}
diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts
new file mode 100644
index 00000000..dbbb4674
--- /dev/null
+++ b/src/ts/process/lua.ts
@@ -0,0 +1,15 @@
+import type { LuaEngine } from "wasmoon";
+import type { character, groupChat } from "../storage/database";
+import { risuChatParser } from "../parser";
+
+export let LuaSafeIds = new Set()
+
+export function registerLuaProcess(engine:LuaEngine, char:character|groupChat) {
+ engine.global.set('cbs', (code:string) => {
+ const parsed = risuChatParser(code, {
+ chara: char,
+ })
+ return parsed
+ })
+}
+
diff --git a/src/ts/process/triggers.ts b/src/ts/process/triggers.ts
index 5e9ccca1..6e044f4c 100644
--- a/src/ts/process/triggers.ts
+++ b/src/ts/process/triggers.ts
@@ -12,6 +12,14 @@ import { HypaProcesser } from "./memory/hypamemory";
import { requestChatData } from "./request";
import { generateAIImage } from "./stableDiff";
import { writeInlayImage } from "./files/image";
+import { LuaEngine, LuaFactory } from "wasmoon";
+import { v4 } from "uuid";
+
+let luaFactory:LuaFactory
+let luaEngine:LuaEngine
+let lastCode = ''
+let LuaSafeIds = new Set()
+let LuaLowLevelIds = new Set()
export interface triggerscript{
comment: string;
@@ -33,7 +41,7 @@ export type triggerConditionsVar = {
}
export type triggerCode = {
- type: 'triggercode',
+ type: 'triggercode'|'triggerlua',
code: string
}
@@ -202,12 +210,15 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{
for(const trigger of triggers){
- if(arg.manualName){
+ if(trigger.effect[0]?.type === 'triggercode' || trigger.effect[0]?.type === 'triggerlua'){
+ //
+ }
+ else if(arg.manualName){
if(trigger.comment !== arg.manualName){
continue
}
}
- else if(mode !== trigger.type && trigger.effect[0]?.type !== 'triggercode'){
+ else if(mode !== trigger.type){
continue
}
@@ -535,7 +546,297 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{
stopSending = true
}
break
+ }
+ case 'triggerlua':{
+ if(!luaEngine || lastCode !== effect.code){
+ if(luaEngine){
+ luaEngine.global.close()
+ }
+ luaFactory = new LuaFactory()
+ luaEngine = await luaFactory.createEngine()
+ luaEngine.global.set('cbs', (code:string) => {
+ const parsed = risuChatParser(code, {
+ chara: char,
+ })
+ return parsed
+ })
+ luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ setVar(key, value)
+ })
+ luaEngine.global.set('getChatVar', (id:string,key:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ return getVar(key)
+ })
+ luaEngine.global.set('stopChat', (id:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ stopSending = true
+ })
+ luaEngine.global.set('alertError', (id:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ alertError(value)
+ })
+ luaEngine.global.set('alertNormal', (id:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ alertNormal(value)
+ })
+ luaEngine.global.set('alertInput', (id:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ return alertInput(value)
+ })
+ luaEngine.global.set('setChat', (id:string, index:number, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ const message = chat.message?.at(index)
+ if(message){
+ message.data = value
+ }
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('setChatRole', (id:string, index:number, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ const message = chat.message?.at(index)
+ if(message){
+ message.role = value === 'user' ? 'user' : 'char'
+ }
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('cutChat', (id:string, start:number, end:number) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ chat.message = chat.message.slice(start,end)
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('addChat', (id:string, role:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
+ chat.message.push({role: roleData, data: value})
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('insertChat', (id:string, index:number, role:string, value:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
+ chat.message.splice(index, 0, {role: roleData, data: value})
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('removeChat', (id:string, index:number) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ chat.message.splice(index, 1)
+ CurrentChat.set(chat)
+ })
+ luaEngine.global.set('getChatLength', (id:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ return chat.message.length
+ })
+ luaEngine.global.set('getFullChat', (id:string) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ return chat.message.map((v) => {
+ return {
+ role: v.role,
+ data: v.data
+ }
+ })
+ })
+ luaEngine.global.set('setFullChat', (id:string, value:any[]) => {
+ if(!LuaSafeIds.has(id)){
+ return
+ }
+ chat.message = value.map((v) => {
+ return {
+ role: v.role,
+ data: v.data
+ }
+ })
+ CurrentChat.set(chat)
+ })
+
+ //Low Level Access
+ luaEngine.global.set('similarity', (id:string, source:string, value:string[]) => {
+ if(!LuaLowLevelIds.has(id)){
+ return
+ }
+ const processer = new HypaProcesser('MiniLM')
+ processer.addText(value)
+ return processer.similaritySearch(source)
+ })
+
+ luaEngine.global.set('generateImage', async (id:string, value:string, negValue:string = '') => {
+ if(!LuaLowLevelIds.has(id)){
+ return
+ }
+ const gen = await generateAIImage(value, char, negValue, 'inlay')
+ if(!gen){
+ return 'Error: Image generation failed'
+ }
+ const imgHTML = new Image()
+ imgHTML.src = gen
+ const inlay = await writeInlayImage(imgHTML)
+ return `{{inlay::${inlay}}}`
+ })
+
+ luaEngine.global.set('LLM', async (id:string, prompt:{
+ role: string,
+ content: string
+ }[]) => {
+ if(!LuaLowLevelIds.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 {
+ 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
+ }
+ })
+
+ luaEngine.global.set('simpleLLM', async (id:string, prompt:string) => {
+ 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
+ }
+ })
+
+ await luaEngine.doString(effect.code)
+ lastCode = effect.code
+ }
+ let accessKey = v4()
+ LuaSafeIds.add(accessKey)
+ if(trigger.lowLevelAccess){
+ LuaLowLevelIds.add(v4())
+ }
+ try {
+ let res:any
+ switch(mode){
+ case 'input':{
+ const func = luaEngine.global.get('onInput')
+ if(func){
+ res = await func(accessKey)
+ }
+ }
+ case 'output':{
+ const func = luaEngine.global.get('onOutput')
+ if(func){
+ res = await func(accessKey)
+ }
+ }
+ case 'start':{
+ const func = luaEngine.global.get('onStart')
+ if(func){
+ res = await func(accessKey)
+ }
+ }
+ case 'manual':{
+ const func = luaEngine.global.get(arg.manualName)
+ if(func){
+ res = await func(accessKey)
+ }
+ }
+ }
+ if(res === false){
+ stopSending = true
+ }
+ } catch (error) {
+
+ alertError('Lua Error: ' + error)
+ console.error(error)
+ }
+
+ LuaSafeIds.delete(accessKey)
+ LuaLowLevelIds.delete(accessKey)
+ break
}
}
}